/* drivers/input/touchscreen/gt9xx.c
 *
 * 2010 - 2013 Goodix Technology.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be a reference
 * to you, when you are integrating the GOODiX's CTP IC into your system,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * Version: 2.2
 * Authors: andrew@goodix.com, meta@goodix.com
 * Release Date: 2014/01/14
 * Revision record:
 *		V1.0:
 *			first Release. By Andrew, 2012/08/31
 *		V1.2:
 *			modify gtp_reset_guitar,slot report,tracking_id & 0x0F. By Andrew,
 * 2012/10/15 V1.4: modify gt9xx_update.c. By Andrew, 2012/12/12 V1.6:
 *			1. new heartbeat/esd_protect mechanism(add external watchdog)
 *			2. doze mode, sliding wakeup
 *			3. 3 more cfg_group(GT9 Sensor_ID: 0~5)
 *			3. config length verification
 *			4. names & comments
 *					By Meta, 2013/03/11
 *		V1.8:
 *			1. pen/stylus identification
 *			2. read double check & fixed config support
 *			3. new esd & slide wakeup optimization
 *					By Meta, 2013/06/08
 *		V2.0:
 *			1. compatible with GT9XXF
 *			2. send config after resume
 *					By Meta, 2013/08/06
 *		V2.2:
 *			1. gt9xx_config for debug
 *			2. gesture wakeup
 *			3. pen separate input device, active-pen button support
 *			4. coordinates & keys optimization
 *					By Meta, 2014/01/14
 */

#include "gt9xx.h"
#include <linux/irq.h>
#include <linux/pm_runtime.h>

#ifdef CONFIG_PM
#include <linux/pm.h>
#endif

#if GTP_ICS_SLOT_REPORT
#include <linux/input/mt.h>
#endif

// if use i2c detect, set 1
#define USE_DETECT 0
static int gUseDetect;
static int startup(void);

#define KEY_NUM 8
static const char *goodix_ts_name = "goodix-ts";
static struct workqueue_struct *goodix_wq;
struct i2c_client *i2c_connect_client;
u8 config[GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH] = {GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff};
unsigned int ctp_key_list[KEY_NUM] = {
	KEY_SWITCHVIDEOMODE, KEY_KBDILLUMTOGGLE, KEY_KBDILLUMDOWN, KEY_KBDILLUMUP, KEY_SEND,
	KEY_REPLY,			 KEY_FORWARDMAIL,	 KEY_SAVE};

#if GTP_HAVE_TOUCH_KEY
static const u16 touch_key_array[] = GTP_KEY_TAB;
#define GTP_MAX_KEY_NUM (sizeof(touch_key_array) / sizeof(touch_key_array[0]))

#if GTP_DEBUG_ON
static const int key_codes[] = {KEY_HOME, KEY_BACK, KEY_MENU, KEY_SEARCH};
static const char *key_names[] = {"Key_Home", "Key_Back", "Key_Menu", "Key_Search"};
#endif

#endif

static s8 gtp_i2c_test(struct i2c_client *client);
void gtp_reset_guitar(struct i2c_client *client, s32 ms);
s32 gtp_send_cfg(struct i2c_client *client);
void gtp_int_sync(s32 ms);

static ssize_t gt91xx_config_read_proc(struct file *, char __user *, size_t, loff_t *);
static ssize_t gt91xx_config_write_proc(struct file *, const char __user *, size_t, loff_t *);

static struct proc_dir_entry *gt91xx_config_proc;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)

static const struct proc_ops config_proc_ops = {
	.proc_read = gt91xx_config_read_proc,
	.proc_write = gt91xx_config_write_proc,
};
#else
static const struct file_operations config_proc_ops = {
	.owner = THIS_MODULE,
	.read = gt91xx_config_read_proc,
	.write = gt91xx_config_write_proc,
};
#endif

#ifdef CONFIG_HAS_EARLYSUSPEND
static void goodix_ts_early_suspend(struct early_suspend *h);
static void goodix_ts_late_resume(struct early_suspend *h);
#endif

#if GTP_CREATE_WR_NODE
extern s32 init_wr_node(struct i2c_client *);
extern void uninit_wr_node(void);
#endif

#if GTP_AUTO_UPDATE
extern u8 gup_init_update_proc(struct goodix_ts_data *);
#endif

#if GTP_ESD_PROTECT
static struct delayed_work gtp_esd_check_work;
static struct workqueue_struct *gtp_esd_check_workqueue;
static void gtp_esd_check_func(struct work_struct *);
static s32 gtp_init_ext_watchdog(struct i2c_client *client);
void gtp_esd_switch(struct i2c_client *, s32);
#endif

/*********** For GT9XXF Start **********/
#if GTP_COMPATIBLE_MODE
extern s32 i2c_read_bytes(struct i2c_client *client, u16 addr, u8 *buf, s32 len);
extern s32 i2c_write_bytes(struct i2c_client *client, u16 addr, u8 *buf, s32 len);
extern s32 gup_clk_calibration(void);
extern s32 gup_fw_download_proc(void *dir, u8 dwn_mode);
extern u8 gup_check_fs_mounted(char *path_name);

void gtp_recovery_reset(struct i2c_client *client);
static s32 gtp_esd_recovery(struct i2c_client *client);
s32 gtp_fw_startup(struct i2c_client *client);
static s32 gtp_main_clk_proc(struct goodix_ts_data *ts);
static s32 gtp_bak_ref_proc(struct goodix_ts_data *ts, u8 mode);

#endif
/********** For GT9XXF End **********/

/*#if GTP_GESTURE_WAKEUP*/
static bool gtp_gesture_wakeup;
static bool gtp_power_ctrl_sleep;
static bool g_suspend_flag;

typedef enum {
	DOZE_DISABLED = 0,
	DOZE_ENABLED = 1,
	DOZE_WAKEUP = 2,
} DOZE_T;
static DOZE_T doze_status = DOZE_DISABLED;
static s8 gtp_enter_doze(struct goodix_ts_data *ts);
/*#endif*/

static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id);
struct goodix_ts_data *gts;
int request_irq_time;
bool enable_irq_time;

u8 grp_cfg_version;

/********************Add by zhongjian for sunxi tp***********************/
#define CTP_IRQ_NUMBER (config_info.int_number)
#define CTP_IRQ_MODE (IRQF_TRIGGER_FALLING)
#define CTP_NAME ("gt9xxnew_ts")
#define SCREEN_MAX_X (screen_max_x)
#define SCREEN_MAX_Y (screen_max_y)
#define PRESS_MAX (255)
static int screen_max_x;
static int screen_max_y;
static int revert_x_flag;
static int revert_y_flag;
static int exchange_x_y_flag;
static __u32 twi_id;
// static u32 debug_mask = 0x1;
static u32 debug_mask;
enum {
	DEBUG_INIT = 1U << 0,
	DEBUG_SUSPEND = 1U << 1,
	DEBUG_INT_INFO = 1U << 2,
	DEBUG_X_Y_INFO = 1U << 3,
	DEBUG_KEY_INFO = 1U << 4,
	DEBUG_WAKEUP_INFO = 1U << 5,
	DEBUG_OTHERS_INFO = 1U << 6,
};
#define dprintk(level_mask, fmt, arg...)   \
	do { \
		if (unlikely(debug_mask & level_mask)) \
			printk("***CTP***" fmt, ##arg); \
	} while (0)

module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
static const unsigned short normal_i2c[3] = {0x14, 0x5d, I2C_CLIENT_END};
struct ctp_config_info config_info = {
	.input_type = CTP_TYPE,
	.name = NULL,
	.int_number = 0,
};

/**
 * ctp_detect - Device detection callback for automatic device creation
 * return value:
 *					  = 0; success;
 *					  < 0; err
 */
static int ctp_detect(struct i2c_client *client, struct i2c_board_info *info)
{
	struct i2c_adapter *adapter = client->adapter;
	int ret = -1;
	printk("the adapter number is %d\n", adapter->nr);

	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
		printk("======return=====\n");
		return -ENODEV;
	}

	if (twi_id == adapter->nr) {
		printk("%s: addr = %x\n", __func__, client->addr);
		ret = gtp_i2c_test(client);
		printk("detect ret %d\n", ret);
		if (!ret) {
			printk("%s:I2C connection might be something wrong \n", __func__);
			return -ENODEV;
		} else {
			strlcpy(info->type, CTP_NAME, I2C_NAME_SIZE);
			printk("======detect ok !=====\n");
			return 0;
		}
	} else {
		return -ENODEV;
	}
}

/**
 * ctp_print_info - sysconfig print function
 * return value:
 *
 */
void ctp_print_info(struct ctp_config_info info, int debug_level)
{
	if (debug_level == DEBUG_INIT) {
		dprintk(DEBUG_INIT, "info.ctp_used:%d\n", info.ctp_used);
		dprintk(DEBUG_INIT, "info.twi_id:%d\n", info.twi_id);
		dprintk(DEBUG_INIT, "info.screen_max_x:%d\n", info.screen_max_x);
		dprintk(DEBUG_INIT, "info.screen_max_y:%d\n", info.screen_max_y);
		dprintk(DEBUG_INIT, "info.revert_x_flag:%d\n", info.revert_x_flag);
		dprintk(DEBUG_INIT, "info.revert_y_flag:%d\n", info.revert_y_flag);
		dprintk(DEBUG_INIT, "info.exchange_x_y_flag:%d\n", info.exchange_x_y_flag);
		dprintk(DEBUG_INIT, "info.irq_gpio_number:%d\n", info.irq_gpio.gpio);
		dprintk(DEBUG_INIT, "info.wakeup_gpio_number:%d\n", info.wakeup_gpio.gpio);
	}
}

/**
 * ctp_wakeup - function
 *
 */
int ctp_wakeup(int status, int ms)
{
	dprintk(DEBUG_INIT, "***CTP*** %s:status:%d,ms = %d\n", __func__, status, ms);

	if (status == 0) {
		if (ms == 0) {
			__gpio_set_value(config_info.wakeup_gpio.gpio, 0);
		} else {
			__gpio_set_value(config_info.wakeup_gpio.gpio, 0);
			msleep(ms);
			__gpio_set_value(config_info.wakeup_gpio.gpio, 1);
		}
	}
	if (status == 1) {
		if (ms == 0) {
			__gpio_set_value(config_info.wakeup_gpio.gpio, 1);
		} else {
			__gpio_set_value(config_info.wakeup_gpio.gpio, 1);
			msleep(ms);
			__gpio_set_value(config_info.wakeup_gpio.gpio, 0);
		}
	}
	msleep(5);

	return 0;
}

void gtp_set_int_value(int status) { gpio_direction_output(config_info.irq_gpio.gpio, status); }

void gtp_set_io_int(void)
{
	int ret;
	if (request_irq_time == 1) {
		input_free_int(&(config_info.input_type), gts);
	}
	ret = input_request_int(&(config_info.input_type), goodix_ts_irq_handler, CTP_IRQ_MODE, gts);
	if (!enable_irq_time) {
		input_set_int_enable(&(config_info.input_type), 0);
	}
	if (ret < 0) {
		printk("set pin to irq mode fail! func = %s line =%d \n", __func__, __LINE__);
	}
	if (request_irq_time == 0) {
		input_free_int(&(config_info.input_type), gts);
	}
}

void gtp_io_init(int ms)
{
	ctp_wakeup(0, 0);
	msleep(ms);
	gtp_set_int_value(0);
	msleep(2);

	ctp_wakeup(1, 0);
	msleep(6);
	gpio_direction_output(config_info.wakeup_gpio.gpio, 1);
	gtp_int_sync(50);
}

/*******************************************************************************/
static ssize_t gtp_gesture_enable_store(struct device *dev, struct device_attribute *attr, const char *buf,
										size_t count)
{
	unsigned long data;
	/*struct input_dev *input = dev_get_drvdata(dev);*/
	/*struct goodix_ts_data *ts = container_of(&input, struct goodix_ts_data,
	 * input_dev);*/
	data = simple_strtoul(buf, NULL, 10);
	dprintk(DEBUG_INIT, "%s g_suspend_flag=%d,gtp_gesture_wakeup=%d,enable=%ld\n", __func__, g_suspend_flag,
			gtp_gesture_wakeup, data);
	if (!g_suspend_flag) {
		if (data) {
			gtp_gesture_wakeup = 1;
			gtp_power_ctrl_sleep = 0;
			dprintk(DEBUG_INIT, "%s gtp_gesture_wakeup=%d,enable=%ld\n", __func__, gtp_gesture_wakeup, data);
		} else {
			gtp_gesture_wakeup = 0;
			gtp_power_ctrl_sleep = 1;
			dprintk(DEBUG_INIT, "%s gtp_gesture_wakeup=%d,enable=%ld\n", __func__, gtp_gesture_wakeup, data);
		}
	} else
		dprintk(DEBUG_INIT,
				"%s gtp is suspend!!!! gtp_gesture_wakeup cann't be set!! "
				"gtp_gesture_wakeup=%d,enable=%ld\n",
				__func__, gtp_gesture_wakeup, data);
	return count;
}
static ssize_t gtp_gesture_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	int enable;
	if (gtp_gesture_wakeup)
		enable = 1;
	else
		enable = 0;
	return sprintf(buf, "%d\n", enable);
}
static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR | S_IWGRP, gtp_gesture_enable_show, gtp_gesture_enable_store);
static struct attribute *gtp_attributes[] = {&dev_attr_enable.attr, NULL};
static struct attribute_group gtp_attribute_group = {.attrs = gtp_attributes};

/*******************************************************
Function:
	Read data from the i2c slave device.
Input:
	client:		i2c device.
	buf[0~1]:	read start address.
	buf[2~len-1]:	read data buffer.
	len:	GTP_ADDR_LENGTH + read bytes count
Output:
	numbers of i2c_msgs to transfer:
	  2: succeed, otherwise: failed
*********************************************************/
s32 gtp_i2c_read(struct i2c_client *client, u8 *buf, s32 len)
{
	struct i2c_msg msgs[2];
	s32 ret = -1;
	s32 retries = 0;

	GTP_DEBUG_FUNC();

	msgs[0].flags = !I2C_M_RD;
	msgs[0].addr = client->addr;
	msgs[0].len = GTP_ADDR_LENGTH;
	msgs[0].buf = &buf[0];
	/*msgs[0].scl_rate = 300 * 1000;*/ /* for Rockchip, etc.*/

	msgs[1].flags = I2C_M_RD;
	msgs[1].addr = client->addr;
	msgs[1].len = len - GTP_ADDR_LENGTH;
	msgs[1].buf = &buf[GTP_ADDR_LENGTH];
	/*msgs[1].scl_rate = 300 * 1000;*/

	while (retries < 5) {
		ret = i2c_transfer(client->adapter, msgs, 2);
		if (ret == 2)
			break;
		retries++;
	}
	if ((retries >= 5)) {
#if GTP_COMPATIBLE_MODE
		struct goodix_ts_data *ts = i2c_get_clientdata(client);
#endif

		/*#if GTP_GESTURE_WAKEUP*/
		if (gtp_gesture_wakeup) {
			/* reset chip would quit doze mode*/
			if (DOZE_ENABLED == doze_status) {
				return ret;
			}
		}
		/* #endif*/
		GTP_ERROR("I2C Read: 0x%04X, %d bytes failed, errcode: %d! Process reset.", (((u16)(buf[0] << 8)) | buf[1]),
				  len - 2, ret);
#if GTP_COMPATIBLE_MODE
		if (CHIP_TYPE_GT9F == ts->chip_type) {
			gtp_recovery_reset(client);
		} else
#endif
		{
			gtp_reset_guitar(client, 10);
		}
	}
	return ret;
}

/*******************************************************
Function:
	Write data to the i2c slave device.
Input:
	client:		i2c device.
	buf[0~1]:	write start address.
	buf[2~len-1]:	data buffer
	len:	GTP_ADDR_LENGTH + write bytes count
Output:
	numbers of i2c_msgs to transfer:
		1: succeed, otherwise: failed
*********************************************************/
s32 gtp_i2c_write(struct i2c_client *client, u8 *buf, s32 len)
{
	struct i2c_msg msg;
	s32 ret = -1;
	s32 retries = 0;

	GTP_DEBUG_FUNC();

	msg.flags = !I2C_M_RD;
	msg.addr = client->addr;
	msg.len = len;
	msg.buf = buf;
	/*msg.scl_rate = 300 * 1000;*/ /* for Rockchip, etc*/

	while (retries < 5) {
		ret = i2c_transfer(client->adapter, &msg, 1);
		if (ret == 1)
			break;
		retries++;
	}
	if ((retries >= 5)) {
#if GTP_COMPATIBLE_MODE
		struct goodix_ts_data *ts = i2c_get_clientdata(client);
#endif

		/*#if GTP_GESTURE_WAKEUP*/
		if (gtp_gesture_wakeup) {
			if (DOZE_ENABLED == doze_status) {
				return ret;
			}
		}
		/*#endif*/
		GTP_ERROR("I2C Write: 0x%04X, %d bytes failed, errcode: %d! Process reset.", (((u16)(buf[0] << 8)) | buf[1]),
				  len - 2, ret);
#if GTP_COMPATIBLE_MODE
		if (CHIP_TYPE_GT9F == ts->chip_type) {
			gtp_recovery_reset(client);
		} else
#endif
		{
			gtp_reset_guitar(client, 10);
		}
	}
	return ret;
}

/*******************************************************
Function:
	i2c read twice, compare the results
Input:
	client:  i2c device
	addr:	 operate address
	rxbuf:	 read data to store, if compare successful
	len:	 bytes to read
Output:
	FAIL:	 read failed
	SUCCESS: read successful
*********************************************************/
s32 gtp_i2c_read_dbl_check(struct i2c_client *client, u16 addr, u8 *rxbuf, int len)
{
	u8 buf[16] = {0};
	u8 confirm_buf[16] = {0};
	u8 retry = 0;

	while (retry++ < 3) {
		memset(buf, 0xAA, 16);
		buf[0] = (u8)(addr >> 8);
		buf[1] = (u8)(addr & 0xFF);
		gtp_i2c_read(client, buf, len + 2);

		memset(confirm_buf, 0xAB, 16);
		confirm_buf[0] = (u8)(addr >> 8);
		confirm_buf[1] = (u8)(addr & 0xFF);
		gtp_i2c_read(client, confirm_buf, len + 2);

		if (!memcmp(buf, confirm_buf, len + 2)) {
			memcpy(rxbuf, confirm_buf + 2, len);
			return SUCCESS;
		}
	}
	GTP_ERROR("I2C read 0x%04X, %d bytes, double check failed!", addr, len);
	return FAIL;
}

/*******************************************************
Function:
	Send config.
Input:
	client: i2c device.
Output:
	result of i2c write operation.
		1: succeed, otherwise: failed
*********************************************************/

s32 gtp_send_cfg(struct i2c_client *client)
{
	s32 ret = 2;

#if GTP_DRIVER_SEND_CFG
	s32 retry = 0;
	struct goodix_ts_data *ts = i2c_get_clientdata(client);

	if (ts->fixed_cfg) {
		GTP_INFO("Ic fixed config, no config sent!");
		return 0;
	} else if (ts->pnl_init_error) {
		GTP_INFO("Error occured in init_panel, no config sent");
		return 0;
	}

	GTP_INFO("Driver send config.");
	for (retry = 0; retry < 5; retry++) {
		ret = gtp_i2c_write(client, config, GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH);
		if (ret > 0) {
			break;
		}
	}
#endif
	return ret;
}
/*******************************************************
Function:
	Disable irq function
Input:
	ts: goodix i2c_client private data
Output:
	None.
*********************************************************/
void gtp_irq_disable(struct goodix_ts_data *ts)
{
	unsigned long irqflags;
	int ret;

	GTP_DEBUG_FUNC();

	dprintk(DEBUG_INT_INFO, "%s ---start!---\n", __func__);
	spin_lock_irqsave(&ts->irq_lock, irqflags);
	if (!ts->irq_is_disable) {
		ts->irq_is_disable = 1;
		if (enable_irq_time) {
			ret = input_set_int_enable(&(config_info.input_type), 0);
			enable_irq_time = false;
			if (ret < 0)
				dprintk(DEBUG_OTHERS_INFO, "%s irq disable failed\n", goodix_ts_name);
		}
	}
	spin_unlock_irqrestore(&ts->irq_lock, irqflags);
}

/*******************************************************
Function:
	Enable irq function
Input:
	ts: goodix i2c_client private data
Output:
	None.
*********************************************************/
void gtp_irq_enable(struct goodix_ts_data *ts)
{
	unsigned long irqflags = 0;
	int ret;

	GTP_DEBUG_FUNC();

	spin_lock_irqsave(&ts->irq_lock, irqflags);
	if (ts->irq_is_disable) {
		ts->irq_is_disable = 0;
		if (!enable_irq_time) {
			ret = input_set_int_enable(&(config_info.input_type), 1);
			enable_irq_time = true;
			if (ret < 0)
				dprintk(DEBUG_OTHERS_INFO, "%s irq enable failed\n", goodix_ts_name);
		}
	}
	spin_unlock_irqrestore(&ts->irq_lock, irqflags);
}

/*******************************************************
Function:
	Report touch point event
Input:
	ts: goodix i2c_client private data
	id: trackId
	x:	input x coordinate
	y:	input y coordinate
	w:	input pressure
Output:
	None.
*********************************************************/
static void gtp_touch_down(struct goodix_ts_data *ts, s32 id, s32 x, s32 y, s32 w)
{
	dprintk(DEBUG_X_Y_INFO, "source data:ID:%d, X:%d, Y:%d, W:%d\n", id, x, y, w);
	if (1 == exchange_x_y_flag) {
		swap(x, y);
	}
	if (1 == revert_x_flag) {
		x = SCREEN_MAX_X - x;
	}
	if (1 == revert_y_flag) {
		y = SCREEN_MAX_Y - y;
	}
	dprintk(DEBUG_X_Y_INFO, "report data:ID:%d, X:%d, Y:%d, W:%d\n", id, x, y, w);

#if GTP_ICS_SLOT_REPORT
	input_mt_slot(ts->input_dev, id);
	input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id);
	input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
	input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);
	input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w);
	input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w);
#else
	input_report_key(ts->input_dev, BTN_TOUCH, 1);
	input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
	input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);
	input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w);
	input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w);
	input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id);
	input_mt_sync(ts->input_dev);
#endif

	GTP_DEBUG("ID:%d, X:%d, Y:%d, W:%d", id, x, y, w);
}

/*******************************************************
Function:
	Report touch release event
Input:
	ts: goodix i2c_client private data
Output:
	None.
*********************************************************/
static void gtp_touch_up(struct goodix_ts_data *ts, s32 id)
{
#if GTP_ICS_SLOT_REPORT
	input_mt_slot(ts->input_dev, id);
	input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1);
	GTP_DEBUG("Touch id[%2d] release!", id);
#else
	input_report_key(ts->input_dev, BTN_TOUCH, 0);
#endif
}

#if GTP_WITH_PEN

static void gtp_pen_init(struct goodix_ts_data *ts)
{
	s32 ret = 0;

	GTP_INFO("Request input device for pen/stylus.");

	ts->pen_dev = input_allocate_device();
	if (ts->pen_dev == NULL) {
		GTP_ERROR("Failed to allocate input device for pen/stylus.");
		return;
	}

	ts->pen_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);

#if GTP_ICS_SLOT_REPORT
	input_mt_init_slots(ts->pen_dev, 16);  //
#else
	ts->pen_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
#endif

	set_bit(BTN_TOOL_PEN, ts->pen_dev->keybit);
	set_bit(INPUT_PROP_DIRECT, ts->pen_dev->propbit);
	// set_bit(INPUT_PROP_POINTER, ts->pen_dev->propbit);

#if GTP_PEN_HAVE_BUTTON
	input_set_capability(ts->pen_dev, EV_KEY, BTN_STYLUS);
	input_set_capability(ts->pen_dev, EV_KEY, BTN_STYLUS2);
#endif

	input_set_abs_params(ts->pen_dev, ABS_MT_POSITION_X, 0, ts->abs_x_max, 0, 0);
	input_set_abs_params(ts->pen_dev, ABS_MT_POSITION_Y, 0, ts->abs_y_max, 0, 0);
	input_set_abs_params(ts->pen_dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
	input_set_abs_params(ts->pen_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
	input_set_abs_params(ts->pen_dev, ABS_MT_TRACKING_ID, 0, 255, 0, 0);

	ts->pen_dev->name = "goodix-pen";
	ts->pen_dev->id.bustype = BUS_I2C;

	ret = input_register_device(ts->pen_dev);
	if (ret) {
		GTP_ERROR("Register %s input device failed", ts->pen_dev->name);
		return;
	}
}

static void gtp_pen_down(s32 x, s32 y, s32 w, s32 id)
{
	struct goodix_ts_data *ts = i2c_get_clientdata(i2c_connect_client);

#if GTP_CHANGE_X2Y
	GTP_SWAP(x, y);
#endif

	input_report_key(ts->pen_dev, BTN_TOOL_PEN, 1);
#if GTP_ICS_SLOT_REPORT
	input_mt_slot(ts->pen_dev, id);
	input_report_abs(ts->pen_dev, ABS_MT_TRACKING_ID, id);
	input_report_abs(ts->pen_dev, ABS_MT_POSITION_X, x);
	input_report_abs(ts->pen_dev, ABS_MT_POSITION_Y, y);
	input_report_abs(ts->pen_dev, ABS_MT_PRESSURE, w);
	input_report_abs(ts->pen_dev, ABS_MT_TOUCH_MAJOR, w);
#else
	input_report_key(ts->pen_dev, BTN_TOUCH, 1);
	input_report_abs(ts->pen_dev, ABS_MT_POSITION_X, x);
	input_report_abs(ts->pen_dev, ABS_MT_POSITION_Y, y);
	input_report_abs(ts->pen_dev, ABS_MT_PRESSURE, w);
	input_report_abs(ts->pen_dev, ABS_MT_TOUCH_MAJOR, w);
	input_report_abs(ts->pen_dev, ABS_MT_TRACKING_ID, id);
	input_mt_sync(ts->pen_dev);
#endif
	GTP_DEBUG("(%d)(%d, %d)[%d]", id, x, y, w);
}

static void gtp_pen_up(s32 id)
{
	struct goodix_ts_data *ts = i2c_get_clientdata(i2c_connect_client);

	input_report_key(ts->pen_dev, BTN_TOOL_PEN, 0);

#if GTP_ICS_SLOT_REPORT
	input_mt_slot(ts->pen_dev, id);
	input_report_abs(ts->pen_dev, ABS_MT_TRACKING_ID, -1);
#else

	input_report_key(ts->pen_dev, BTN_TOUCH, 0);
#endif
}
#endif

/*******************************************************
Function:
	Goodix touchscreen work function
Input:
	work: work struct of goodix_workqueue
Output:
	None.
*********************************************************/
static void goodix_ts_work_func(struct work_struct *work)
{
	u8 end_cmd[3] = {GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF, 0};
	u8 point_data[2 + 1 + 8 * GTP_MAX_TOUCH + 1] = {GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF};
	u8 touch_num = 0;
	u8 finger = 0;
	static u16 pre_touch;
	static u8 pre_key;
#if GTP_WITH_PEN
	u8 pen_active = 0;
	static u8 pre_pen;
#endif
	u8 key_value = 0;
	u8 *coor_data = NULL;
	s32 input_x = 0;
	s32 input_y = 0;
	s32 input_w = 0;
	s32 id = 0;
	s32 i = 0;
	s32 ret = -1;
	struct goodix_ts_data *ts = NULL;

#if GTP_COMPATIBLE_MODE
	u8 rqst_buf[3] = {0x80, 0x43};	// for GT9XXF
#endif

	//#if GTP_GESTURE_WAKEUP
	u8 doze_buf[3] = {0x81, 0x4B};
	//#endif

	GTP_DEBUG_FUNC();
	dprintk(DEBUG_X_Y_INFO, "===enter %s===\n", __func__);
	ts = container_of(work, struct goodix_ts_data, work);
	if (ts->enter_update) {
		return;
	}
	if (ts->input_dev == NULL) {
		GTP_DEBUG("input dev = null return now! \n");
		goto exit_work_func;
	}
	//#if GTP_GESTURE_WAKEUP
	if (gtp_gesture_wakeup) {
		if (DOZE_ENABLED == doze_status) {
			ret = gtp_i2c_read(i2c_connect_client, doze_buf, 3);
			GTP_DEBUG("0x814B = 0x%02X", doze_buf[2]);
			if (ret > 0) {
				if ((doze_buf[2] == 'a') || (doze_buf[2] == 'b') || (doze_buf[2] == 'c') || (doze_buf[2] == 'd') ||
					(doze_buf[2] == 'e') || (doze_buf[2] == 'g') || (doze_buf[2] == 'h') || (doze_buf[2] == 'm') ||
					(doze_buf[2] == 'o') || (doze_buf[2] == 'q') || (doze_buf[2] == 's') || (doze_buf[2] == 'v') ||
					(doze_buf[2] == 'w') || (doze_buf[2] == 'y') || (doze_buf[2] == 'z') ||
					(doze_buf[2] == 0x5E) /* ^ */
				) {
					if (doze_buf[2] != 0x5E) {
						GTP_INFO("Wakeup by gesture(%c), light up the screen!", doze_buf[2]);
					} else {
						GTP_INFO("Wakeup by gesture(^), light up the screen!");
					}
					doze_status = DOZE_WAKEUP;
					input_report_key(ts->input_dev, KEY_POWER, 1);
					input_sync(ts->input_dev);
					input_report_key(ts->input_dev, KEY_POWER, 0);
					input_sync(ts->input_dev);
					switch (doze_buf[2]) {
					case 'e':
						input_report_key(ts->input_dev, ctp_key_list[0], 1);
						input_sync(ts->input_dev);
						input_report_key(ts->input_dev, ctp_key_list[0], 0);
						input_sync(ts->input_dev);
						break;
					case 'o':
						input_report_key(ts->input_dev, ctp_key_list[1], 1);
						input_sync(ts->input_dev);
						input_report_key(ts->input_dev, ctp_key_list[1], 0);
						input_sync(ts->input_dev);
						break;
					case 'w':
						input_report_key(ts->input_dev, ctp_key_list[3], 1);
						input_sync(ts->input_dev);
						input_report_key(ts->input_dev, ctp_key_list[3], 0);
						input_sync(ts->input_dev);
						break;
					case 'c':
						input_report_key(ts->input_dev, ctp_key_list[2], 1);
						input_sync(ts->input_dev);
						input_report_key(ts->input_dev, ctp_key_list[2], 0);
						input_sync(ts->input_dev);
						break;
					default:
						break;
					}
					// clear 0x814B
					doze_buf[2] = 0x00;
					gtp_i2c_write(i2c_connect_client, doze_buf, 3);
				} else if ((doze_buf[2] == 0xAA) || (doze_buf[2] == 0xBB) || (doze_buf[2] == 0xAB) ||
						   (doze_buf[2] == 0xBA)) {
					char *direction[4] = {"Right", "Down", "Up", "Left"};
					u8 type = ((doze_buf[2] & 0x0F) - 0x0A) + (((doze_buf[2] >> 4) & 0x0F) - 0x0A) * 2;

					GTP_INFO("%s slide to light up the screen!", direction[type]);
					doze_status = DOZE_WAKEUP;
					input_report_key(ts->input_dev, KEY_POWER, 1);
					input_sync(ts->input_dev);
					input_report_key(ts->input_dev, KEY_POWER, 0);
					input_sync(ts->input_dev);
					switch (type) {
					case 0:
						input_report_key(ts->input_dev, ctp_key_list[7], 1);
						input_sync(ts->input_dev);
						input_report_key(ts->input_dev, ctp_key_list[7], 0);
						input_sync(ts->input_dev);
						break;
					case 1:
						input_report_key(ts->input_dev, ctp_key_list[5], 1);
						input_sync(ts->input_dev);
						input_report_key(ts->input_dev, ctp_key_list[5], 0);
						input_sync(ts->input_dev);
						break;
					case 2:
						input_report_key(ts->input_dev, ctp_key_list[4], 1);
						input_sync(ts->input_dev);
						input_report_key(ts->input_dev, ctp_key_list[4], 0);
						input_sync(ts->input_dev);
						break;
					case 3:
						input_report_key(ts->input_dev, ctp_key_list[6], 1);
						input_sync(ts->input_dev);
						input_report_key(ts->input_dev, ctp_key_list[6], 0);
						input_sync(ts->input_dev);
						break;
					default:
						break;
					}
					// clear 0x814B
					doze_buf[2] = 0x00;
					gtp_i2c_write(i2c_connect_client, doze_buf, 3);
				} else if (0xCC == doze_buf[2]) {
					GTP_INFO("Double click to light up the screen!");
					doze_status = DOZE_WAKEUP;
					input_report_key(ts->input_dev, KEY_POWER, 1);
					input_sync(ts->input_dev);
					input_report_key(ts->input_dev, KEY_POWER, 0);
					input_sync(ts->input_dev);
					// clear 0x814B
					doze_buf[2] = 0x00;
					gtp_i2c_write(i2c_connect_client, doze_buf, 3);
				} else {
					// clear 0x814B
					doze_buf[2] = 0x00;
					gtp_i2c_write(i2c_connect_client, doze_buf, 3);
					gtp_enter_doze(ts);
				}
			}
			if (ts->use_irq) {
				gtp_irq_enable(ts);
			}
			return;
		}
	}
	//#endif

	ret = gtp_i2c_read(ts->client, point_data, 12);
	if (ret < 0) {
		GTP_ERROR("I2C transfer error. errno:%d\n ", ret);
		if (ts->use_irq) {
			gtp_irq_enable(ts);
		}
		return;
	}

	finger = point_data[GTP_ADDR_LENGTH];

#if GTP_COMPATIBLE_MODE
	// GT9XXF
	if ((finger == 0x00) && (CHIP_TYPE_GT9F == ts->chip_type)) {	// request arrived
		ret = gtp_i2c_read(ts->client, rqst_buf, 3);
		if (ret < 0) {
			GTP_ERROR("Read request status error!");
			goto exit_work_func;
		}

		switch (rqst_buf[2]) {
		case GTP_RQST_CONFIG:
			GTP_INFO("Request for config.");
			ret = gtp_send_cfg(ts->client);
			if (ret < 0) {
				GTP_ERROR("Request for config unresponded!");
			} else {
				rqst_buf[2] = GTP_RQST_RESPONDED;
				gtp_i2c_write(ts->client, rqst_buf, 3);
				GTP_INFO("Request for config responded!");
			}
			break;

		case GTP_RQST_BAK_REF:
			GTP_INFO("Request for backup reference.");
			ts->rqst_processing = 1;
			ret = gtp_bak_ref_proc(ts, GTP_BAK_REF_SEND);
			if (SUCCESS == ret) {
				rqst_buf[2] = GTP_RQST_RESPONDED;
				gtp_i2c_write(ts->client, rqst_buf, 3);
				ts->rqst_processing = 0;
				GTP_INFO("Request for backup reference responded!");
			} else {
				GTP_ERROR("Requeset for backup reference unresponed!");
			}
			break;

		case GTP_RQST_RESET:
			GTP_INFO("Request for reset.");
			gtp_recovery_reset(ts->client);
			break;

		case GTP_RQST_MAIN_CLOCK:
			GTP_INFO("Request for main clock.");
			ts->rqst_processing = 1;
			ret = gtp_main_clk_proc(ts);
			if (FAIL == ret) {
				GTP_ERROR("Request for main clock unresponded!");
			} else {
				GTP_INFO("Request for main clock responded!");
				rqst_buf[2] = GTP_RQST_RESPONDED;
				gtp_i2c_write(ts->client, rqst_buf, 3);
				ts->rqst_processing = 0;
				ts->clk_chk_fs_times = 0;
			}
			break;

		default:
			GTP_INFO("Undefined request: 0x%02X", rqst_buf[2]);
			rqst_buf[2] = GTP_RQST_RESPONDED;
			gtp_i2c_write(ts->client, rqst_buf, 3);
			break;
		}
	}
#endif
	if (finger == 0x00) {
		if (ts->use_irq) {
			gtp_irq_enable(ts);
		}
		return;
	}

	if ((finger & 0x80) == 0) {
		goto exit_work_func;
	}

	touch_num = finger & 0x0f;
	if (touch_num > GTP_MAX_TOUCH) {
		goto exit_work_func;
	}

	if (touch_num > 1) {
		u8 buf[8 * GTP_MAX_TOUCH] = {(GTP_READ_COOR_ADDR + 10) >> 8, (GTP_READ_COOR_ADDR + 10) & 0xff};

		ret = gtp_i2c_read(ts->client, buf, 2 + 8 * (touch_num - 1));
		memcpy(&point_data[12], &buf[2], 8 * (touch_num - 1));
	}

#if (GTP_HAVE_TOUCH_KEY || GTP_PEN_HAVE_BUTTON)
	key_value = point_data[3 + 8 * touch_num];

	if (key_value || pre_key) {
#if GTP_PEN_HAVE_BUTTON
		if (key_value == 0x40) {
			GTP_DEBUG("BTN_STYLUS & BTN_STYLUS2 Down.");
			input_report_key(ts->pen_dev, BTN_STYLUS, 1);
			input_report_key(ts->pen_dev, BTN_STYLUS2, 1);
			pen_active = 1;
		} else if (key_value == 0x10) {
			GTP_DEBUG("BTN_STYLUS Down, BTN_STYLUS2 Up.");
			input_report_key(ts->pen_dev, BTN_STYLUS, 1);
			input_report_key(ts->pen_dev, BTN_STYLUS2, 0);
			pen_active = 1;
		} else if (key_value == 0x20) {
			GTP_DEBUG("BTN_STYLUS Up, BTN_STYLUS2 Down.");
			input_report_key(ts->pen_dev, BTN_STYLUS, 0);
			input_report_key(ts->pen_dev, BTN_STYLUS2, 1);
			pen_active = 1;
		} else {
			GTP_DEBUG("BTN_STYLUS & BTN_STYLUS2 Up.");
			input_report_key(ts->pen_dev, BTN_STYLUS, 0);
			input_report_key(ts->pen_dev, BTN_STYLUS2, 0);
			if ((pre_key == 0x40) || (pre_key == 0x20) || (pre_key == 0x10)) {
				pen_active = 1;
			}
		}
		if (pen_active) {
			touch_num = 0;	// shield pen point
							// pre_touch = 0;	 // clear last pen status
		}
#endif

#if GTP_HAVE_TOUCH_KEY
		if (!pre_touch) {
			for (i = 0; i < GTP_MAX_KEY_NUM; i++) {
#if GTP_DEBUG_ON
				for (ret = 0; ret < 4; ++ret) {
					if (key_codes[ret] == touch_key_array[i]) {
						GTP_DEBUG("Key: %s %s", key_names[ret], (key_value & (0x01 << i)) ? "Down" : "Up");
						break;
					}
				}
#endif
				input_report_key(ts->input_dev, touch_key_array[i], key_value & (0x01 << i));
			}
			touch_num = 0;	// shield fingers
		}
#endif
	}
#endif
	pre_key = key_value;

	GTP_DEBUG("pre_touch:%02x, finger:%02x.", pre_touch, finger);

#if GTP_ICS_SLOT_REPORT

#if GTP_WITH_PEN
	if (pre_pen && (touch_num == 0)) {
		GTP_DEBUG("Pen touch UP(Slot)!");
		gtp_pen_up(0);
		pen_active = 1;
		pre_pen = 0;
	}
#endif
	if (pre_touch || touch_num) {
		s32 pos = 0;
		u16 touch_index = 0;
		u8 report_num = 0;
		coor_data = &point_data[3];

		if (touch_num) {
			id = coor_data[pos] & 0x0F;

#if GTP_WITH_PEN
			id = coor_data[pos];
			if ((id & 0x80)) {
				GTP_DEBUG("Pen touch DOWN(Slot)!");
				input_x = coor_data[pos + 1] | (coor_data[pos + 2] << 8);
				input_y = coor_data[pos + 3] | (coor_data[pos + 4] << 8);
				input_w = coor_data[pos + 5] | (coor_data[pos + 6] << 8);

				gtp_pen_down(input_x, input_y, input_w, 0);
				pre_pen = 1;
				pre_touch = 0;
				pen_active = 1;
			}
#endif

			touch_index |= (0x01 << id);
		}

		GTP_DEBUG("id = %d,touch_index = 0x%x, pre_touch = 0x%x\n", id, touch_index, pre_touch);
		for (i = 0; i < GTP_MAX_TOUCH; i++) {
#if GTP_WITH_PEN
			if (pre_pen == 1) {
				break;
			}
#endif

			if ((touch_index & (0x01 << i))) {
				input_x = coor_data[pos + 1] | (coor_data[pos + 2] << 8);
				input_y = coor_data[pos + 3] | (coor_data[pos + 4] << 8);
				input_w = coor_data[pos + 5] | (coor_data[pos + 6] << 8);

				gtp_touch_down(ts, id, input_x, input_y, input_w);
				pre_touch |= 0x01 << i;

				report_num++;
				if (report_num < touch_num) {
					pos += 8;
					id = coor_data[pos] & 0x0F;
					touch_index |= (0x01 << id);
				}
			} else {
				gtp_touch_up(ts, i);
				pre_touch &= ~(0x01 << i);
			}
		}
	}
#else

	if (touch_num) {
		for (i = 0; i < touch_num; i++) {
			coor_data = &point_data[i * 8 + 3];

			id = coor_data[0] & 0x0F;
			input_x = coor_data[1] | (coor_data[2] << 8);
			input_y = coor_data[3] | (coor_data[4] << 8);
			input_w = coor_data[5] | (coor_data[6] << 8);

#if GTP_WITH_PEN
			id = coor_data[0];
			if (id & 0x80) {
				GTP_DEBUG("Pen touch DOWN!");
				gtp_pen_down(input_x, input_y, input_w, 0);
				pre_pen = 1;
				pen_active = 1;
				break;
			} else
#endif
			{
				gtp_touch_down(ts, id, input_x, input_y, input_w);
			}
		}
	} else if (pre_touch) {
#if GTP_WITH_PEN
		if (pre_pen == 1) {
			GTP_DEBUG("Pen touch UP!");
			gtp_pen_up(0);
			pre_pen = 0;
			pen_active = 1;
		} else
#endif
		{
			GTP_DEBUG("Touch Release!");
			gtp_touch_up(ts, 0);
		}
	}

	pre_touch = touch_num;
#endif

#if GTP_WITH_PEN
	if (pen_active) {
		pen_active = 0;
		input_sync(ts->pen_dev);
	} else
#endif
	{
		input_sync(ts->input_dev);
	}

exit_work_func:
	if (!ts->gtp_rawdiff_mode) {
		ret = gtp_i2c_write(ts->client, end_cmd, 3);
		if (ret < 0) {
			GTP_INFO("I2C write end_cmd error!");
		}
	}
	if (ts->use_irq) {
		gtp_irq_enable(ts);
	}
}

/*******************************************************
Function:
	Timer interrupt service routine for polling mode.
Input:
	timer: timer struct pointer
Output:
	Timer work mode.
		HRTIMER_NORESTART: no restart mode
*********************************************************/
static enum hrtimer_restart goodix_ts_timer_handler(struct hrtimer *timer)
{
	struct goodix_ts_data *ts = container_of(timer, struct goodix_ts_data, timer);

	GTP_DEBUG_FUNC();

	queue_work(goodix_wq, &ts->work);
	hrtimer_start(&ts->timer, ktime_set(0, (GTP_POLL_TIME + 6) * 1000000), HRTIMER_MODE_REL);
	return HRTIMER_NORESTART;
}

/*******************************************************
Function:
	External interrupt service routine for interrupt mode.
Input:
	irq:  interrupt number.
	dev_id: private data pointer
Output:
	Handle Result.
		IRQ_HANDLED: interrupt handled successfully
*********************************************************/
static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
{
	struct goodix_ts_data *ts = dev_id;

	GTP_DEBUG_FUNC();

	gtp_irq_disable(ts);

	queue_work(goodix_wq, &ts->work);

	return IRQ_HANDLED;
}
/*******************************************************
Function:
	Synchronization.
Input:
	ms: synchronization time in millisecond.
Output:
	None.
*******************************************************/
void gtp_int_sync(s32 ms)
{
	gtp_set_int_value(0);
	msleep(ms);
	gpio_direction_input(config_info.irq_gpio.gpio);
	gtp_set_io_int();
}

/*******************************************************
Function:
	Reset chip.
Input:
	ms: reset time in millisecond
Output:
	None.
*******************************************************/
void gtp_reset_guitar(struct i2c_client *client, s32 ms)
{
#if GTP_COMPATIBLE_MODE
	struct goodix_ts_data *ts = i2c_get_clientdata(client);
#endif

	GTP_DEBUG_FUNC();
	GTP_INFO("Guitar reset");
	__gpio_set_value(config_info.wakeup_gpio.gpio,
					 0);  // begin select I2C slave addr
	msleep(ms);			  // T2: > 10ms
						  // HIGH: 0x28/0x29, LOW: 0xBA/0xBB
	if (client->addr == 0x14)
		gtp_set_int_value(1);
	else
		gtp_set_int_value(0);

	msleep(2);	// T3: > 100us
	__gpio_set_value(config_info.wakeup_gpio.gpio, 1);

	msleep(6);	// T4: > 5ms

	//	  gpio_direction_input(config_info.wakeup_gpio.gpio);	 // end select I2C
	//	  slave addr

#if GTP_COMPATIBLE_MODE
	if (CHIP_TYPE_GT9F == ts->chip_type) {
		return;
	}
#endif

	gtp_int_sync(50);
#if GTP_ESD_PROTECT
	gtp_init_ext_watchdog(client);
#endif
}

//#if GTP_GESTURE_WAKEUP
/*******************************************************
Function:
	Enter doze mode for sliding wakeup.
Input:
	ts: goodix tp private data
Output:
	1: succeed, otherwise failed
*******************************************************/
static s8 gtp_enter_doze(struct goodix_ts_data *ts)
{
	s8 ret = -1;
	s8 retry = 0;
	u8 i2c_control_buf[3] = {(u8)(GTP_REG_SLEEP >> 8), (u8)GTP_REG_SLEEP, 8};

	GTP_DEBUG_FUNC();

	GTP_DEBUG("Entering gesture mode.");
	while (retry++ < 5) {
		i2c_control_buf[0] = 0x80;
		i2c_control_buf[1] = 0x46;
		ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
		if (ret < 0) {
			GTP_DEBUG("failed to set doze flag into 0x8046, %d", retry);
			continue;
		}
		i2c_control_buf[0] = 0x80;
		i2c_control_buf[1] = 0x40;
		ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
		if (ret > 0) {
			doze_status = DOZE_ENABLED;
			GTP_INFO("Gesture mode enabled.");
			return ret;
		}
		msleep(10);
	}
	GTP_ERROR("GTP send gesture cmd failed.");
	return ret;
}
//#else
/*******************************************************
Function:
	Enter sleep mode.
Input:
	ts: private data.
Output:
	Executive outcomes.
	   1: succeed, otherwise failed.
*******************************************************/
static s8 gtp_enter_sleep(struct goodix_ts_data *ts)
{
	s8 ret = -1;
	s8 retry = 0;
	u8 i2c_control_buf[3] = {(u8)(GTP_REG_SLEEP >> 8), (u8)GTP_REG_SLEEP, 5};

#if GTP_COMPATIBLE_MODE
	u8 status_buf[3] = {0x80, 0x44};
#endif

	GTP_DEBUG_FUNC();

#if GTP_COMPATIBLE_MODE
	if (CHIP_TYPE_GT9F == ts->chip_type) {
		// GT9XXF: host interact with ic
		ret = gtp_i2c_read(ts->client, status_buf, 3);
		if (ret < 0) {
			GTP_ERROR("failed to get backup-reference status");
		}

		if (status_buf[2] & 0x80) {
			ret = gtp_bak_ref_proc(ts, GTP_BAK_REF_STORE);
			if (FAIL == ret) {
				GTP_ERROR("failed to store bak_ref");
			}
		}
	}
#endif

	gtp_set_int_value(0);
	msleep(5);

	while (retry++ < 5) {
		ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
		if (ret > 0) {
			GTP_INFO("GTP enter sleep!");

			return ret;
		}
		msleep(10);
	}
	GTP_ERROR("GTP send sleep cmd failed.");
	return ret;
}
//#endif
/*******************************************************
Function:
	Wakeup from sleep.
Input:
	ts: private data.
Output:
	Executive outcomes.
		>0: succeed, otherwise: failed.
*******************************************************/
static s8 gtp_wakeup_sleep(struct goodix_ts_data *ts)
{
	u8 retry = 0;
	s8 ret = -1;

	GTP_DEBUG_FUNC();

#if GTP_COMPATIBLE_MODE
	if (CHIP_TYPE_GT9F == ts->chip_type) {
		u8 opr_buf[3] = {0x41, 0x80};

		gtp_set_int_value(1);
		msleep(5);

		for (retry = 0; retry < 10; ++retry) {
			// hold ss51 & dsp
			opr_buf[2] = 0x0C;
			ret = gtp_i2c_write(ts->client, opr_buf, 3);
			if (FAIL == ret) {
				GTP_ERROR("failed to hold ss51 & dsp!");
				continue;
			}
			opr_buf[2] = 0x00;
			ret = gtp_i2c_read(ts->client, opr_buf, 3);
			if (FAIL == ret) {
				GTP_ERROR("failed to get ss51 & dsp status!");
				continue;
			}
			if (0x0C != opr_buf[2]) {
				GTP_DEBUG("ss51 & dsp not been hold, %d", retry + 1);
				continue;
			}
			GTP_DEBUG("ss51 & dsp confirmed hold");

			ret = gtp_fw_startup(ts->client);
			if (FAIL == ret) {
				GTP_ERROR("failed to startup GT9XXF, process recovery");
				gtp_esd_recovery(ts->client);
			}
			break;
		}
		if (retry >= 10) {
			GTP_ERROR("failed to wakeup, processing esd recovery");
			gtp_esd_recovery(ts->client);
		} else {
			GTP_INFO("GT9XXF gtp wakeup success");
		}
		return ret;
	}
#endif
	//#if GTP_POWER_CTRL_SLEEP
	if (gtp_power_ctrl_sleep)
		while (retry++ < 5) {
			gtp_reset_guitar(ts->client, 20);

			GTP_INFO("GTP wakeup sleep.");
			return 1;
		}
	//#else
	else {
		while (retry++ < 10) {
			//#if GTP_GESTURE_WAKEUP
			if (gtp_gesture_wakeup) {
				if (DOZE_WAKEUP != doze_status) {
					GTP_INFO("Powerkey wakeup.");
				} else {
					GTP_INFO("Gesture wakeup.");
				}
				doze_status = DOZE_DISABLED;
				gtp_irq_disable(ts);
				gtp_reset_guitar(ts->client, 10);
				gtp_irq_enable(ts);
			} else {
				//#else
				gtp_set_int_value(1);
				msleep(5);
				//#endif
			}

			ret = gtp_i2c_test(ts->client);
			if (ret > 0) {
				GTP_INFO("GTP wakeup sleep.");

				// #if (!GTP_GESTURE_WAKEUP)
				if (!gtp_gesture_wakeup) {
					{
						gtp_int_sync(25);
#if GTP_ESD_PROTECT
						gtp_init_ext_watchdog(ts->client);
#endif
					}
				}
				//#endif

				return ret;
			}
			gtp_reset_guitar(ts->client, 20);
		}
	}
	//#endif

	GTP_ERROR("GTP wakeup sleep failed.");
	return ret;
}

#if GTP_DRIVER_SEND_CFG
static s32 gtp_get_info(struct goodix_ts_data *ts)
{
	u8 opr_buf[6] = {0};
	s32 ret = 0;

	ts->abs_x_max = GTP_MAX_WIDTH;
	ts->abs_y_max = GTP_MAX_HEIGHT;
	ts->int_trigger_type = GTP_INT_TRIGGER;

	opr_buf[0] = (u8)((GTP_REG_CONFIG_DATA + 1) >> 8);
	opr_buf[1] = (u8)((GTP_REG_CONFIG_DATA + 1) & 0xFF);

	ret = gtp_i2c_read(ts->client, opr_buf, 6);
	if (ret < 0) {
		return FAIL;
	}

	ts->abs_x_max = (opr_buf[3] << 8) + opr_buf[2];
	ts->abs_y_max = (opr_buf[5] << 8) + opr_buf[4];

	opr_buf[0] = (u8)((GTP_REG_CONFIG_DATA + 6) >> 8);
	opr_buf[1] = (u8)((GTP_REG_CONFIG_DATA + 6) & 0xFF);

	ret = gtp_i2c_read(ts->client, opr_buf, 3);
	if (ret < 0) {
		return FAIL;
	}
	ts->int_trigger_type = opr_buf[2] & 0x03;

	GTP_INFO("X_MAX = %d, Y_MAX = %d, TRIGGER = 0x%02x", ts->abs_x_max, ts->abs_y_max, ts->int_trigger_type);

	return SUCCESS;
}
#endif

/*******************************************************
Function:
	Initialize gtp.
Input:
	ts: goodix private data
Output:
	Executive outcomes.
		0: succeed, otherwise: failed
*******************************************************/
static s32 gtp_init_panel(struct goodix_ts_data *ts)
{
	s32 ret = -1;

#if GTP_DRIVER_SEND_CFG
	s32 i = 0;
	u8 check_sum = 0;
	u8 opr_buf[16] = {0};
	u8 sensor_id = 0;

	u8 cfg_info_group1[] = CTP_CFG_GROUP1;
	u8 cfg_info_group2[] = CTP_CFG_GROUP2;
	u8 cfg_info_group3[] = CTP_CFG_GROUP3;
	u8 cfg_info_group4[] = CTP_CFG_GROUP4;
	u8 cfg_info_group5[] = CTP_CFG_GROUP5;
	u8 cfg_info_group6[] = CTP_CFG_GROUP6;
	u8 cfg_info_group7[] = CTP_CFG_GROUP7;
	u8 cfg_info_group8[] = CTP_CFG_GROUP8;
	u8 cfg_info_group9[] = CTP_CFG_GROUP9;

	u8 *send_cfg_buf[] = {cfg_info_group1, cfg_info_group2, cfg_info_group3, cfg_info_group4,
						  cfg_info_group5, cfg_info_group6, cfg_info_group7, cfg_info_group8, cfg_info_group9};
	u8 cfg_info_len[] = {CFG_GROUP_LEN(cfg_info_group1), CFG_GROUP_LEN(cfg_info_group2), CFG_GROUP_LEN(cfg_info_group3),
						 CFG_GROUP_LEN(cfg_info_group4), CFG_GROUP_LEN(cfg_info_group5), CFG_GROUP_LEN(cfg_info_group6),
						 CFG_GROUP_LEN(cfg_info_group7), CFG_GROUP_LEN(cfg_info_group8), CFG_GROUP_LEN(cfg_info_group9)};

	dprintk(DEBUG_INIT, "Config Groups Lengths: %d, %d, %d, %d, %d, %d, %d, %d, %d\n", cfg_info_len[0], cfg_info_len[1],
			cfg_info_len[2], cfg_info_len[3], cfg_info_len[4], cfg_info_len[5], cfg_info_len[6], cfg_info_len[7], cfg_info_len[8]);

#if GTP_COMPATIBLE_MODE
	if (CHIP_TYPE_GT9F == ts->chip_type) {
		ts->fw_error = 0;
	} else
#endif
	{
		ret = gtp_i2c_read_dbl_check(ts->client, 0x41E4, opr_buf, 1);
		if (SUCCESS == ret) {
			if (opr_buf[0] != 0xBE) {
				ts->fw_error = 1;
				printk("Firmware error, no config sent!\n");
				return -1;
			}
		}
	}

	if ((!cfg_info_len[1]) && (!cfg_info_len[2]) && (!cfg_info_len[3]) && (!cfg_info_len[4]) && (!cfg_info_len[5]) &&
		(!cfg_info_len[6]) && (!cfg_info_len[7]) && (!cfg_info_len[8])) {
		sensor_id = 0;
	} else {
#if GTP_COMPATIBLE_MODE
		msleep(50);
#endif
		//		 ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_SENSOR_ID,
		//		 &sensor_id, 1);
		dprintk(DEBUG_INIT, "CTP name : %s\n", config_info.name);
		if (!strcmp(config_info.name, "gt9271_mb976a9")) {
			sensor_id = 0;
			dprintk(DEBUG_INIT, "gt9xx:sensor_id = %d\n", sensor_id);

		} else if (!strcmp(config_info.name, "gt9110_wt097")) {
			sensor_id = 1;
			dprintk(DEBUG_INIT, "gt9xx:sensor_id = %d\n", sensor_id);

		} else if (!strcmp(config_info.name, "gt9271_wt097")) {
			sensor_id = 2;
			dprintk(DEBUG_INIT, "gt9xx:sensor_id = %d\n", sensor_id);

		} else if (!strcmp(config_info.name, "gt9110_g200")) {
			sensor_id = 3;
			dprintk(DEBUG_INIT, "gt9xx:sensor_id = %d\n", sensor_id);

		} else if (!strcmp(config_info.name, "gt9271_noah")) {
			sensor_id = 4;
			dprintk(DEBUG_INIT, "gt9xx:sensor_id = %d\n", sensor_id);
		} else if (!strcmp(config_info.name, "gt9271_p2")) {
			sensor_id = 5;
			dprintk(DEBUG_INIT, "gt9xx:sensor_id = %d\n", sensor_id);
		} else if (!strcmp(config_info.name, "gt911_1060")) {
			sensor_id = 6;
			dprintk(DEBUG_INIT, "gt9xx:sensor_id = %d\n", sensor_id);
		} else if (!strcmp(config_info.name, "gt911_784")) {
			sensor_id = 7;
			dprintk(DEBUG_INIT, "gt9xx:sensor_id = %d\n", sensor_id);
		} else if (!strcmp(config_info.name, "gt9xxnew_ts")) {
			sensor_id = 8;
			dprintk(DEBUG_INIT, "gt9xx:sensor_id = %d\n", sensor_id);
		} else {
			sensor_id = 0;
			dprintk(DEBUG_INIT, "gt9xx:sensor_id = %d\n", sensor_id);
		}
		ret = SUCCESS;
		if (SUCCESS == ret) {
			if (sensor_id >= 0xff) {
				printk("Invalid sensor_id(0x%02X), No Config Sent!\n", sensor_id);
				ts->pnl_init_error = 1;
				return -1;
			}
		} else {
			printk("Failed to get sensor_id, No config sent!\n");
			ts->pnl_init_error = 1;
			return -1;
		}
		dprintk(DEBUG_INIT, "Sensor_ID: %d", sensor_id);
	}
	ts->gtp_cfg_len = cfg_info_len[sensor_id];
	dprintk(DEBUG_INIT, "CTP_CONFIG_GROUP%d used, config length: %d\n", sensor_id + 1, ts->gtp_cfg_len);

	if (ts->gtp_cfg_len < GTP_CONFIG_MIN_LENGTH) {
		printk(
			"Config Group%d is INVALID CONFIG GROUP(Len: %d)! NO Config Sent! "
			"You need to check you header file CFG_GROUP section!\n",
			sensor_id + 1, ts->gtp_cfg_len);
		ts->pnl_init_error = 1;
		return -1;
	}

#if GTP_COMPATIBLE_MODE
	if (CHIP_TYPE_GT9F == ts->chip_type) {
		ts->fixed_cfg = 0;
	} else
#endif
	{
		ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CONFIG_DATA, &opr_buf[0], 1);

		if (ret == SUCCESS) {
			dprintk(DEBUG_INIT,
					"CFG_GROUP%d Config Version: %d, 0x%02X; IC Config Version: %d, "
					"0x%02X\n",
					sensor_id + 1, send_cfg_buf[sensor_id][0], send_cfg_buf[sensor_id][0], opr_buf[0], opr_buf[0]);

			if (opr_buf[0] < 155) {
				grp_cfg_version = send_cfg_buf[sensor_id][0];  // backup group config version
				send_cfg_buf[sensor_id][0] = 0x00;
				ts->fixed_cfg = 0;
			} else {	// treated as fixed config, not send config
				dprintk(DEBUG_INIT, "Ic fixed config with config version(%d, 0x%02X)\n", opr_buf[0], opr_buf[0]);
				ts->fixed_cfg = 1;
				gtp_get_info(ts);
				return 0;
			}
		} else {
			printk("Failed to get ic config version!No config sent!\n");
			return -1;
		}
	}

	memset(&config[GTP_ADDR_LENGTH], 0, GTP_CONFIG_MAX_LENGTH);
	memcpy(&config[GTP_ADDR_LENGTH], send_cfg_buf[sensor_id], ts->gtp_cfg_len);

#if GTP_CUSTOM_CFG
	config[RESOLUTION_LOC] = (u8)GTP_MAX_WIDTH;
	config[RESOLUTION_LOC + 1] = (u8)(GTP_MAX_WIDTH >> 8);
	config[RESOLUTION_LOC + 2] = (u8)GTP_MAX_HEIGHT;
	config[RESOLUTION_LOC + 3] = (u8)(GTP_MAX_HEIGHT >> 8);

	if (GTP_INT_TRIGGER == 0) { // RISING
		config[TRIGGER_LOC] &= 0xfe;
	} else if (GTP_INT_TRIGGER == 1) {  // FALLING
		config[TRIGGER_LOC] |= 0x01;
	}
#endif	// GTP_CUSTOM_CFG

	check_sum = 0;
	for (i = GTP_ADDR_LENGTH; i < ts->gtp_cfg_len; i++) {
		check_sum += config[i];
	}
	config[ts->gtp_cfg_len] = (~check_sum) + 1;

#else  // driver not send config

	ts->gtp_cfg_len = GTP_CONFIG_MAX_LENGTH;
	ret = gtp_i2c_read(ts->client, config, ts->gtp_cfg_len + GTP_ADDR_LENGTH);
	if (ret < 0) {
		GTP_ERROR("Read Config Failed, Using Default Resolution & INT Trigger!");
		ts->abs_x_max = GTP_MAX_WIDTH;
		ts->abs_y_max = GTP_MAX_HEIGHT;
		ts->int_trigger_type = GTP_INT_TRIGGER;
	}

#endif	// GTP_DRIVER_SEND_CFG

	if ((ts->abs_x_max == 0) && (ts->abs_y_max == 0)) {
		ts->abs_x_max = (config[RESOLUTION_LOC + 1] << 8) + config[RESOLUTION_LOC];
		ts->abs_y_max = (config[RESOLUTION_LOC + 3] << 8) + config[RESOLUTION_LOC + 2];
		ts->int_trigger_type = (config[TRIGGER_LOC]) & 0x03;
	}

#if GTP_COMPATIBLE_MODE
	if (CHIP_TYPE_GT9F == ts->chip_type) {
		u8 sensor_num = 0;
		u8 driver_num = 0;
		u8 have_key = 0;

		have_key = (config[GTP_REG_HAVE_KEY - GTP_REG_CONFIG_DATA + 2] & 0x01);

		if (1 == ts->is_950) {
			driver_num = config[GTP_REG_MATRIX_DRVNUM - GTP_REG_CONFIG_DATA + 2];
			sensor_num = config[GTP_REG_MATRIX_SENNUM - GTP_REG_CONFIG_DATA + 2];
			if (have_key) {
				driver_num--;
			}
			ts->bak_ref_len = (driver_num * (sensor_num - 1) + 2) * 2 * 6;
		} else {
			driver_num = (config[CFG_LOC_DRVA_NUM] & 0x1F) + (config[CFG_LOC_DRVB_NUM] & 0x1F);
			if (have_key) {
				driver_num--;
			}
			sensor_num = (config[CFG_LOC_SENS_NUM] & 0x0F) + ((config[CFG_LOC_SENS_NUM] >> 4) & 0x0F);
			ts->bak_ref_len = (driver_num * (sensor_num - 2) + 2) * 2;
		}

		GTP_INFO("Drv * Sen: %d * %d(key: %d), X_MAX: %d, Y_MAX: %d, TRIGGER: 0x%02x", driver_num, sensor_num, have_key,
				 ts->abs_x_max, ts->abs_y_max, ts->int_trigger_type);
		return 0;
	} else
#endif
	{
#if GTP_DRIVER_SEND_CFG
		ret = gtp_send_cfg(ts->client);
		if (ret < 0) {
			GTP_ERROR("Send config error.");
		}
		// set config version to CTP_CFG_GROUP, for resume to send config
		config[GTP_ADDR_LENGTH] = grp_cfg_version;
		check_sum = 0;
		for (i = GTP_ADDR_LENGTH; i < ts->gtp_cfg_len; i++) {
			check_sum += config[i];
		}
		config[ts->gtp_cfg_len] = (~check_sum) + 1;
#endif
		GTP_INFO("X_MAX: %d, Y_MAX: %d, TRIGGER: 0x%02x", ts->abs_x_max, ts->abs_y_max, ts->int_trigger_type);
	}

	msleep(10);
	return 0;
}

static ssize_t gt91xx_config_read_proc(struct file *file, char __user *page, size_t size, loff_t *ppos)
{
	char *ptr = page;
	char temp_data[GTP_CONFIG_MAX_LENGTH + 2] = {0x80, 0x47};
	int i;

	if (*ppos) {
		return 0;
	}
	ptr += sprintf(ptr, "==== GT9XX config init value====\n");

	for (i = 0; i < GTP_CONFIG_MAX_LENGTH; i++) {
		ptr += sprintf(ptr, "0x%02X ", config[i + 2]);

		if (i % 8 == 7)
			ptr += sprintf(ptr, "\n");
	}

	ptr += sprintf(ptr, "\n");

	ptr += sprintf(ptr, "==== GT9XX config real value====\n");
	gtp_i2c_read(i2c_connect_client, temp_data, GTP_CONFIG_MAX_LENGTH + 2);
	for (i = 0; i < GTP_CONFIG_MAX_LENGTH; i++) {
		ptr += sprintf(ptr, "0x%02X ", temp_data[i + 2]);

		if (i % 8 == 7)
			ptr += sprintf(ptr, "\n");
	}
	*ppos += ptr - page;
	return (ptr - page);
}

static ssize_t gt91xx_config_write_proc(struct file *filp, const char __user *buffer, size_t count, loff_t *off)
{
	s32 ret = 0;

	GTP_DEBUG("write count %u\n", count);

	if (count > GTP_CONFIG_MAX_LENGTH) {
		GTP_ERROR("size not match [%d:%u]\n", GTP_CONFIG_MAX_LENGTH, count);
		return -EFAULT;
	}

	if (copy_from_user(&config[2], buffer, count)) {
		GTP_ERROR("copy from user fail\n");
		return -EFAULT;
	}

	ret = gtp_send_cfg(i2c_connect_client);

	if (ret < 0) {
		GTP_ERROR("send config failed.");
	}

	return count;
}
/*******************************************************
Function:
	Read chip version.
Input:
	client:  i2c device
	version: buffer to keep ic firmware version
Output:
	read operation return.
		2: succeed, otherwise: failed
*******************************************************/
s32 gtp_read_version(struct i2c_client *client, u16 *version)
{
	s32 ret = -1;
	u8 buf[8] = {GTP_REG_VERSION >> 8, GTP_REG_VERSION & 0xff};

	GTP_DEBUG_FUNC();

	ret = gtp_i2c_read(client, buf, sizeof(buf));
	if (ret < 0) {
		GTP_ERROR("GTP read version failed");
		return ret;
	}

	if (version) {
		*version = (buf[7] << 8) | buf[6];
	}
	if (buf[5] == 0x00) {
		GTP_INFO("IC Version: %c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[7], buf[6]);
	} else {
		GTP_INFO("IC Version: %c%c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[5], buf[7], buf[6]);
	}
	return ret;
}

/*******************************************************
Function:
	I2c test Function.
Input:
	client:i2c client.
Output:
	Executive outcomes.
		2: succeed, otherwise failed.
*******************************************************/
static s8 gtp_i2c_test(struct i2c_client *client)
{
	u8 test[3] = {GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff};
	u8 retry = 0;
	s8 ret = -1;

	GTP_DEBUG_FUNC();

	while (retry++ < 5) {
		ret = gtp_i2c_read(client, test, 3);
		if (ret > 0) {
			return ret;
		}
		GTP_ERROR("GTP i2c test failed time %d.", retry);
		msleep(10);
	}
	return ret;
}

/*******************************************************
Function:
	Request interrupt.
Input:
	ts: private data.
Output:
	Executive outcomes.
		0: succeed, -1: failed.
*******************************************************/
static s8 gtp_request_irq(struct goodix_ts_data *ts)
{
	s32 ret = -1;

	ret = input_request_int(&(config_info.input_type), goodix_ts_irq_handler, CTP_IRQ_MODE, ts);

	if (ret) {
		GTP_ERROR("Request IRQ failed!ERRNO:%d.", ret);
		gtp_set_int_value(0);
		// input_free_int(&(config_info.input_type), ts);

		hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
		ts->timer.function = goodix_ts_timer_handler;
		hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
		return -1;
	} else {
		request_irq_time = 1;
		enable_irq_time = true;
		gtp_irq_disable(ts);
		ts->use_irq = 1;
		return 0;
	}
}

/*******************************************************
Function:
	Request input device Function.
Input:
	ts:private data.
Output:
	Executive outcomes.
		0: succeed, otherwise: failed.
*******************************************************/
static s8 gtp_request_input_dev(struct goodix_ts_data *ts)
{
	s8 ret = -1;
	s8 phys[32];
	u8 index = 0;

	GTP_DEBUG_FUNC();

	ts->input_dev = input_allocate_device();
	if (ts->input_dev == NULL) {
		GTP_ERROR("Failed to allocate input device.");
		return -ENOMEM;
	}

	ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
#if GTP_ICS_SLOT_REPORT
	input_mt_init_slots(ts->input_dev, 16);  // in case of "out of memory"
#else
	ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
#endif
	__set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);

#if GTP_HAVE_TOUCH_KEY
	for (index = 0; index < GTP_MAX_KEY_NUM; index++) {
		input_set_capability(ts->input_dev, EV_KEY, touch_key_array[index]);
	}
#endif

	//#if GTP_GESTURE_WAKEUP
	if (gtp_gesture_wakeup)
		input_set_capability(ts->input_dev, EV_KEY, KEY_POWER);
	for (index = 0; index < KEY_NUM; index++) {
		input_set_capability(ts->input_dev, EV_KEY, ctp_key_list[index]);
	}
	//#endif

	if (1 == exchange_x_y_flag)
		swap(ts->abs_x_max, ts->abs_y_max);

	input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, ts->abs_x_max, 0, 0);
	input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, ts->abs_y_max, 0, 0);
	input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
	input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
	input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, 255, 0, 0);

	sprintf(phys, "input/ts");
	ts->input_dev->name = CTP_NAME;
	ts->input_dev->phys = phys;
	ts->input_dev->id.bustype = BUS_I2C;
	ts->input_dev->id.vendor = 0xDEAD;
	ts->input_dev->id.product = 0xBEEF;
	ts->input_dev->id.version = 10427;

	ret = input_register_device(ts->input_dev);
	if (ret) {
		GTP_ERROR("Register %s input device failed", ts->input_dev->name);
		return -ENODEV;
	}
	ret = sysfs_create_group(&ts->input_dev->dev.kobj, &gtp_attribute_group);
	if (ret) {
		GTP_ERROR("create sys failed\n");
	}

#ifdef CONFIG_HAS_EARLYSUSPEND
	ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
	ts->early_suspend.suspend = goodix_ts_early_suspend;
	ts->early_suspend.resume = goodix_ts_late_resume;
	register_early_suspend(&ts->early_suspend);
#endif

#if GTP_WITH_PEN
	gtp_pen_init(ts);
#endif

	return 0;
}

//************** For GT9XXF Start *************//
#if GTP_COMPATIBLE_MODE

s32 gtp_fw_startup(struct i2c_client *client)
{
	u8 opr_buf[4];
	s32 ret = 0;

	// init sw WDT
	opr_buf[0] = 0xAA;
	ret = i2c_write_bytes(client, 0x8041, opr_buf, 1);
	if (ret < 0) {
		return FAIL;
	}

	// release SS51 & DSP
	opr_buf[0] = 0x00;
	ret = i2c_write_bytes(client, 0x4180, opr_buf, 1);
	if (ret < 0) {
		return FAIL;
	}
	// int sync
	gtp_int_sync(25);

	// check fw run status
	ret = i2c_read_bytes(client, 0x8041, opr_buf, 1);
	if (ret < 0) {
		return FAIL;
	}
	if (0xAA == opr_buf[0]) {
		GTP_ERROR("IC works abnormally,startup failed.");
		return FAIL;
	} else {
		GTP_INFO("IC works normally, Startup success.");
		opr_buf[0] = 0xAA;
		i2c_write_bytes(client, 0x8041, opr_buf, 1);
		return SUCCESS;
	}
}

static s32 gtp_esd_recovery(struct i2c_client *client)
{
	s32 retry = 0;
	s32 ret = 0;
	struct goodix_ts_data *ts;

	ts = i2c_get_clientdata(client);

	gtp_irq_disable(ts);

	GTP_INFO("GT9XXF esd recovery mode");
	for (retry = 0; retry < 5; retry++) {
		ret = gup_fw_download_proc(NULL, GTP_FL_ESD_RECOVERY);
		if (FAIL == ret) {
			GTP_ERROR("esd recovery failed %d", retry + 1);
			continue;
		}
		ret = gtp_fw_startup(ts->client);
		if (FAIL == ret) {
			GTP_ERROR("GT9XXF start up failed %d", retry + 1);
			continue;
		}
		break;
	}
	gtp_irq_enable(ts);

	if (retry >= 5) {
		GTP_ERROR("failed to esd recovery");
		return FAIL;
	}

	GTP_INFO("Esd recovery successful");
	return SUCCESS;
}

void gtp_recovery_reset(struct i2c_client *client)
{
#if GTP_ESD_PROTECT
	gtp_esd_switch(client, SWITCH_OFF);
#endif
	GTP_DEBUG_FUNC();

	gtp_esd_recovery(client);

#if GTP_ESD_PROTECT
	gtp_esd_switch(client, SWITCH_ON);
#endif
}

static s32 gtp_bak_ref_proc(struct goodix_ts_data *ts, u8 mode)
{
	s32 ret = 0;
	s32 i = 0;
	s32 j = 0;
	u16 ref_sum = 0;
	u16 learn_cnt = 0;
	u16 chksum = 0;
	s32 ref_seg_len = 0;
	s32 ref_grps = 0;
	struct file *ref_filp = NULL;
	u8 *p_bak_ref;

	ret = gup_check_fs_mounted("/data");
	if (FAIL == ret) {
		ts->ref_chk_fs_times++;
		GTP_DEBUG("Ref check /data times/MAX_TIMES: %d / %d", ts->ref_chk_fs_times, GTP_CHK_FS_MNT_MAX);
		if (ts->ref_chk_fs_times < GTP_CHK_FS_MNT_MAX) {
			msleep(50);
			GTP_INFO("/data not mounted.");
			return FAIL;
		}
		GTP_INFO("check /data mount timeout...");
	} else {
		GTP_INFO("/data mounted!!!(%d/%d)", ts->ref_chk_fs_times, GTP_CHK_FS_MNT_MAX);
	}

	p_bak_ref = (u8 *)kzalloc(ts->bak_ref_len, GFP_KERNEL);

	if (NULL == p_bak_ref) {
		GTP_ERROR("Allocate memory for p_bak_ref failed!");
		return FAIL;
	}

	if (ts->is_950) {
		ref_seg_len = ts->bak_ref_len / 6;
		ref_grps = 6;
	} else {
		ref_seg_len = ts->bak_ref_len;
		ref_grps = 1;
	}
	ref_filp = filp_open(GTP_BAK_REF_PATH, O_RDWR | O_CREAT, 0666);
	if (IS_ERR(ref_filp)) {
		GTP_ERROR("Failed to open/create %s.", GTP_BAK_REF_PATH);
		if (GTP_BAK_REF_SEND == mode) {
			goto bak_ref_default;
		} else {
			goto bak_ref_exit;
		}
	}

	switch (mode) {
	case GTP_BAK_REF_SEND:
		GTP_INFO("Send backup-reference");
		ref_filp->f_op->llseek(ref_filp, 0, SEEK_SET);
		ret = ref_filp->f_op->read(ref_filp, (char *)p_bak_ref, ts->bak_ref_len, &ref_filp->f_pos);
		if (ret < 0) {
			GTP_ERROR("failed to read bak_ref info from file, sending defualt bak_ref");
			goto bak_ref_default;
		}
		for (j = 0; j < ref_grps; ++j) {
			ref_sum = 0;
			for (i = 0; i < (ref_seg_len); i += 2) {
				ref_sum += (p_bak_ref[i + j * ref_seg_len] << 8) + p_bak_ref[i + 1 + j * ref_seg_len];
			}
			learn_cnt = (p_bak_ref[j * ref_seg_len + ref_seg_len - 4] << 8) +
						(p_bak_ref[j * ref_seg_len + ref_seg_len - 3]);
			chksum = (p_bak_ref[j * ref_seg_len + ref_seg_len - 2] << 8) +
					 (p_bak_ref[j * ref_seg_len + ref_seg_len - 1]);
			GTP_DEBUG("learn count = %d", learn_cnt);
			GTP_DEBUG("chksum = %d", chksum);
			GTP_DEBUG("ref_sum = 0x%04X", ref_sum & 0xFFFF);
			// Sum(1~ref_seg_len) == 1
			if (1 != ref_sum) {
				GTP_INFO("wrong chksum for bak_ref, reset to 0x00 bak_ref");
				memset(&p_bak_ref[j * ref_seg_len], 0, ref_seg_len);
				p_bak_ref[ref_seg_len + j * ref_seg_len - 1] = 0x01;
			} else {
				if (j == (ref_grps - 1)) {
					GTP_INFO("backup-reference data in %s used", GTP_BAK_REF_PATH);
				}
			}
		}
		ret = i2c_write_bytes(ts->client, GTP_REG_BAK_REF, p_bak_ref, ts->bak_ref_len);
		if (FAIL == ret) {
			GTP_ERROR("failed to send bak_ref because of iic comm error");
			goto bak_ref_exit;
		}
		break;

	case GTP_BAK_REF_STORE:
		GTP_INFO("Store backup-reference");
		ret = i2c_read_bytes(ts->client, GTP_REG_BAK_REF, p_bak_ref, ts->bak_ref_len);
		if (ret < 0) {
			GTP_ERROR("failed to read bak_ref info, sending default back-reference");
			goto bak_ref_default;
		}
		ref_filp->f_op->llseek(ref_filp, 0, SEEK_SET);
		ref_filp->f_op->write(ref_filp, (char *)p_bak_ref, ts->bak_ref_len, &ref_filp->f_pos);
		break;

	default:
		GTP_ERROR("invalid backup-reference request");
		break;
	}
	ret = SUCCESS;
	goto bak_ref_exit;

bak_ref_default:

	for (j = 0; j < ref_grps; ++j) {
		memset(&p_bak_ref[j * ref_seg_len], 0, ref_seg_len);
		p_bak_ref[j * ref_seg_len + ref_seg_len - 1] = 0x01;  // checksum = 1
	}
	ret = i2c_write_bytes(ts->client, GTP_REG_BAK_REF, p_bak_ref, ts->bak_ref_len);
	if (!IS_ERR(ref_filp)) {
		GTP_INFO("write backup-reference data into %s", GTP_BAK_REF_PATH);
		ref_filp->f_op->llseek(ref_filp, 0, SEEK_SET);
		ref_filp->f_op->write(ref_filp, (char *)p_bak_ref, ts->bak_ref_len, &ref_filp->f_pos);
	}
	if (ret == FAIL) {
		GTP_ERROR("failed to load the default backup reference");
	}

bak_ref_exit:

	if (p_bak_ref) {
		kfree(p_bak_ref);
	}
	if (ref_filp && !IS_ERR(ref_filp)) {
		filp_close(ref_filp, NULL);
	}
	return ret;
}

static s32 gtp_verify_main_clk(u8 *p_main_clk)
{
	u8 chksum = 0;
	u8 main_clock = p_main_clk[0];
	s32 i = 0;

	if (main_clock < 50 || main_clock > 120) {
		return FAIL;
	}

	for (i = 0; i < 5; ++i) {
		if (main_clock != p_main_clk[i]) {
			return FAIL;
		}
		chksum += p_main_clk[i];
	}
	chksum += p_main_clk[5];
	if ((chksum) == 0) {
		return SUCCESS;
	} else {
		return FAIL;
	}
}

static s32 gtp_main_clk_proc(struct goodix_ts_data *ts)
{
	s32 ret = 0;
	s32 i = 0;
	s32 clk_chksum = 0;
	struct file *clk_filp = NULL;
	u8 p_main_clk[6] = {0};

	ret = gup_check_fs_mounted("/data");
	if (FAIL == ret) {
		ts->clk_chk_fs_times++;
		GTP_DEBUG("Clock check /data times/MAX_TIMES: %d / %d", ts->clk_chk_fs_times, GTP_CHK_FS_MNT_MAX);
		if (ts->clk_chk_fs_times < GTP_CHK_FS_MNT_MAX) {
			msleep(50);
			GTP_INFO("/data not mounted.");
			return FAIL;
		}
		GTP_INFO("Check /data mount timeout!");
	} else {
		GTP_INFO("/data mounted!!!(%d/%d)", ts->clk_chk_fs_times, GTP_CHK_FS_MNT_MAX);
	}

	clk_filp = filp_open(GTP_MAIN_CLK_PATH, O_RDWR | O_CREAT, 0666);
	if (IS_ERR(clk_filp)) {
		GTP_ERROR("%s is unavailable, calculate main clock", GTP_MAIN_CLK_PATH);
	} else {
		clk_filp->f_op->llseek(clk_filp, 0, SEEK_SET);
		clk_filp->f_op->read(clk_filp, (char *)p_main_clk, 6, &clk_filp->f_pos);

		ret = gtp_verify_main_clk(p_main_clk);
		if (FAIL == ret) {
			// recalculate main clock & rewrite main clock data to file
			GTP_ERROR("main clock data in %s is wrong, recalculate main clock", GTP_MAIN_CLK_PATH);
		} else {
			GTP_INFO("main clock data in %s used, main clock freq: %d", GTP_MAIN_CLK_PATH, p_main_clk[0]);
			filp_close(clk_filp, NULL);
			goto update_main_clk;
		}
	}

#if GTP_ESD_PROTECT
	gtp_esd_switch(ts->client, SWITCH_OFF);
#endif
	ret = gup_clk_calibration();
	gtp_esd_recovery(ts->client);

#if GTP_ESD_PROTECT
	gtp_esd_switch(ts->client, SWITCH_ON);
#endif

	GTP_INFO("calibrate main clock: %d", ret);
	if (ret < 50 || ret > 120) {
		GTP_ERROR("wrong main clock: %d", ret);
		goto exit_main_clk;
	}

	// Sum{0x8020~0x8025} = 0
	for (i = 0; i < 5; ++i) {
		p_main_clk[i] = ret;
		clk_chksum += p_main_clk[i];
	}
	p_main_clk[5] = 0 - clk_chksum;

	if (!IS_ERR(clk_filp)) {
		GTP_DEBUG("write main clock data into %s", GTP_MAIN_CLK_PATH);
		clk_filp->f_op->llseek(clk_filp, 0, SEEK_SET);
		clk_filp->f_op->write(clk_filp, (char *)p_main_clk, 6, &clk_filp->f_pos);
		filp_close(clk_filp, NULL);
	}

update_main_clk:
	ret = i2c_write_bytes(ts->client, GTP_REG_MAIN_CLK, p_main_clk, 6);
	if (FAIL == ret) {
		GTP_ERROR("update main clock failed!");
		return FAIL;
	}
	return SUCCESS;

exit_main_clk:
	if (!IS_ERR(clk_filp)) {
		filp_close(clk_filp, NULL);
	}
	return FAIL;
}

s32 gtp_gt9xxf_init(struct i2c_client *client)
{
	s32 ret = 0;

	ret = gup_fw_download_proc(NULL, GTP_FL_FW_BURN);
	if (FAIL == ret) {
		return FAIL;
	}

	ret = gtp_fw_startup(client);
	if (FAIL == ret) {
		return FAIL;
	}
	return SUCCESS;
}

void gtp_get_chip_type(struct goodix_ts_data *ts)
{
	u8 opr_buf[10] = {0x00};
	s32 ret = 0;

	msleep(10);

	ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CHIP_TYPE, opr_buf, 10);

	if (FAIL == ret) {
		GTP_ERROR("Failed to get chip-type, set chip type default: GOODIX_GT9");
		ts->chip_type = CHIP_TYPE_GT9;
		return;
	}

	if (!memcmp(opr_buf, "GOODIX_GT9", 10)) {
		ts->chip_type = CHIP_TYPE_GT9;
	} else {	// GT9XXF
		ts->chip_type = CHIP_TYPE_GT9F;
	}
	GTP_INFO("Chip Type: %s", (ts->chip_type == CHIP_TYPE_GT9) ? "GOODIX_GT9" : "GOODIX_GT9F");
}

#endif
//************* For GT9XXF End ************//

/*******************************************************
Function:
	I2c probe.
Input:
	client: i2c device struct.
	id: device id.
Output:
	Executive outcomes.
		0: succeed.
*******************************************************/
static int goodix_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	s32 ret = -1;
	// struct goodix_ts_data *ts;
	u16 version_info;

	// do NOT remove these logs
	dprintk(DEBUG_INIT, "GTP Driver Version: %s", GTP_DRIVER_VERSION);
	dprintk(DEBUG_INIT, "GTP I2C Address: 0x%02x", client->addr);
	// printk("GTP driver init func = %s,line = %d \n",__func__,__LINE__);
	config_info.probed = 0;

	i2c_connect_client = client;

	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
		printk("I2C check functionality failed.\n");
		return -ENODEV;
	}
	if (config_info.dev == NULL)
		config_info.dev = &client->dev;

#if !USE_DETECT
	if (!gUseDetect && startup()) {
		printk("can not startup device!\n");
		goto fail;
	}
#endif
	gts = kzalloc(sizeof(*gts), GFP_KERNEL);
	if (gts == NULL) {
		printk("Alloc GFP_KERNEL memory failed.\n");
		goto fail;
	}

	memset(gts, 0, sizeof(*gts));
	INIT_WORK(&gts->work, goodix_ts_work_func);
	gts->client = client;

	spin_lock_init(&gts->irq_lock);  // 2.6.39 later
	// ts->irq_lock = SPIN_LOCK_UNLOCKED;	// 2.6.39 & before
	gtp_io_init(20);
#if GTP_ESD_PROTECT
	gts->clk_tick_cnt = 2 * HZ;  // HZ: clock ticks in 1 second generated by system
	GTP_DEBUG("Clock ticks for an esd cycle: %d", ts->clk_tick_cnt);
	spin_lock_init(&gts->esd_lock);
	// ts->esd_lock = SPIN_LOCK_UNLOCKED;
#endif
	i2c_set_clientdata(client, gts);

	gts->gtp_rawdiff_mode = 0;
	gts->gtp_is_suspend = 0;

#if GTP_COMPATIBLE_MODE
	gtp_get_chip_type(ts);

	if (CHIP_TYPE_GT9F == gts->chip_type) {
		ret = gtp_gt9xxf_init(gts->client);
		if (FAIL == ret) {
			printk("Failed to init GT9XXF.");
		}
	}
#endif
#if GTP_I2C_TEST
	ret = gtp_i2c_test(client);
	if (ret < 0) {
		printk("I2C communication ERROR!");
		goto fail;
	}
#endif
	ret = gtp_read_version(client, &version_info);
	if (ret < 0) {
		printk("Read version failed.");
	}

	printk("GTP driver init func = %s,line = %d \n", __func__, __LINE__);
	ret = gtp_init_panel(gts);
	if (ret < 0) {
		printk("GTP init panel failed.");
	}

	// printk("GTP driver init func = %s,line = %d \n",__func__,__LINE__);
	// Create proc file system
	gt91xx_config_proc = proc_create(GT91XX_CONFIG_PROC_FILE, 0666, NULL, &config_proc_ops);
	if (gt91xx_config_proc == NULL) {
		GTP_ERROR("create_proc_entry %s failed\n", GT91XX_CONFIG_PROC_FILE);
	} else {
		GTP_INFO("create proc entry %s success", GT91XX_CONFIG_PROC_FILE);
	}

#if GTP_AUTO_UPDATE
	ret = gup_init_update_proc(gts);
	if (ret < 0) {
		GTP_ERROR("Create update thread error.");
	}
#endif

	ret = gtp_request_input_dev(gts);
	if (ret < 0) {
		GTP_ERROR("GTP request input dev failed");
	}

	ret = gtp_request_irq(gts);
	if (ret < 0) {
		GTP_INFO("GTP works in polling mode.");
	} else {
		GTP_INFO("GTP works in interrupt mode.");
	}

	// printk("GTP driver init func = %s,line = %d \n",__func__,__LINE__);
	if (gts->use_irq) {
		gtp_irq_enable(gts);
	}

#if GTP_CREATE_WR_NODE
	init_wr_node(client);
#endif

#if GTP_ESD_PROTECT
	gtp_esd_switch(client, SWITCH_ON);
#endif

	pm_runtime_set_active(&client->dev);
	pm_runtime_get(&client->dev);
	pm_runtime_enable(&client->dev);
	config_info.probed = 1;

	// printk("GTP driver init func = %s,line = %d \n",__func__,__LINE__);
	// gts = ts;
	return 0;

fail:
	input_sensor_free(&(config_info.input_type));
	return -1;
}

/*******************************************************
Function:
	Goodix touchscreen driver release function.
Input:
	client: i2c device struct.
Output:
	Executive outcomes. 0---succeed.
*******************************************************/
static int goodix_ts_remove(struct i2c_client *client)
{
	struct goodix_ts_data *ts = i2c_get_clientdata(client);

	GTP_DEBUG_FUNC();

#ifdef CONFIG_HAS_EARLYSUSPEND
	unregister_early_suspend(&ts->early_suspend);
#endif

#if GTP_CREATE_WR_NODE
	uninit_wr_node();
#endif

#if GTP_ESD_PROTECT
	destroy_workqueue(gtp_esd_check_workqueue);
#endif
	pm_runtime_disable(&client->dev);
	pm_runtime_set_suspended(&client->dev);
	remove_proc_entry(GT91XX_CONFIG_PROC_FILE, NULL);
	sysfs_remove_group(&ts->input_dev->dev.kobj, &gtp_attribute_group);

	if (ts) {
		if (ts->use_irq) {
			gtp_set_int_value(0);
			input_free_int(&(config_info.input_type), ts);
		} else {
			hrtimer_cancel(&ts->timer);
		}
	} else {
		printk("error!!! ts is null!\n");
		return -1;
	}

	GTP_INFO("GTP driver removing...");
	i2c_set_clientdata(client, NULL);
	input_unregister_device(ts->input_dev);
	kfree(ts);

	return 0;
}

#ifdef CONFIG_PM
/*******************************************************
Function:
	Early suspend function.
Input:
	h: early_suspend struct.
Output:
	None.
*******************************************************/
static void goodix_ts_suspend(struct goodix_ts_data *ts)
{
	s8 ret = -1;

	GTP_DEBUG_FUNC();
	GTP_INFO("System suspend.");

	ts->gtp_is_suspend = 1;
	g_suspend_flag = 1;
	dprintk(DEBUG_INIT, "%s g_suspend_flag=%d\n", __func__, g_suspend_flag);

#if GTP_ESD_PROTECT
	gtp_esd_switch(ts->client, SWITCH_OFF);
#endif

	// 2021.1.7 remove enter sleep mode action
	if (gtp_gesture_wakeup)
		ret = gtp_enter_doze(ts);
	else {
		if (ts->use_irq) {
			gtp_irq_disable(ts);
		} else {
			hrtimer_cancel(&ts->timer);
		}
		// ret = gtp_enter_sleep(ts);
	}
	// if (ret < 0) {
	//	GTP_ERROR("GTP suspend failed.");
	//}
	/* to avoid waking up while not sleeping
				delay 48 + 10ms to ensure reliability */
	// msleep(58);

	if (!gtp_gesture_wakeup) {
		printk("ctp power off suspend!!!\n");
		input_set_power_enable(&(config_info.input_type), 0);
		__gpio_set_value(config_info.wakeup_gpio.gpio, 0);
	}
}

/*******************************************************
Function:
	Late resume function.
Input:
	h: early_suspend struct.
Output:
	None.
*******************************************************/
static void goodix_ts_resume(struct goodix_ts_data *ts)
{
	s8 ret = -1;
	GTP_DEBUG_FUNC();

	dprintk(DEBUG_SUSPEND, "System resume\n");
	if (!gtp_gesture_wakeup) {
		__gpio_set_value(config_info.wakeup_gpio.gpio, 1);
		dprintk(DEBUG_INIT, "ctp power on in goodix_ts_resume!!!\n");
		input_set_power_enable(&(config_info.input_type), 1);
		msleep(10);
	}

	ret = gtp_wakeup_sleep(ts);
	if (gtp_gesture_wakeup)
		doze_status = DOZE_DISABLED;

	if (ret < 0) {
		dprintk(DEBUG_INIT, "GTP later resume failed.");
	}
#if (GTP_COMPATIBLE_MODE)
	if (CHIP_TYPE_GT9F == ts->chip_type) {
		/* do nothing */
	} else
#endif
	{
		gtp_send_cfg(ts->client);
	}
	printk("ts->use_irq=%d", ts->use_irq);
	if (ts->use_irq) {
		gtp_irq_enable(ts);
	} else {
		hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
	}

	ts->gtp_is_suspend = 0;
	g_suspend_flag = 0;
	dprintk(DEBUG_INIT, "%s g_suspend_flag=%d\n", __func__, g_suspend_flag);
#if GTP_ESD_PROTECT
	gtp_esd_switch(ts->client, SWITCH_ON);
#endif
}

static int gtp_pm_suspend(struct device *dev)
{
	struct goodix_ts_data *ts = dev_get_drvdata(dev);
	dprintk(DEBUG_SUSPEND, "CONFIG_PM:enter gtp_pm_suspend. \n");
	if (pm_runtime_suspended(dev))
		return 0;

	if (ts) {
		goodix_ts_suspend(ts);
	}

	return 0;
}
static int gtp_pm_resume(struct device *dev)
{
	struct goodix_ts_data *ts = dev_get_drvdata(dev);
	dprintk(DEBUG_SUSPEND, "CONFIG_PM:enter laterresume: goodix_ts_resume. \n");
	if (pm_runtime_suspended(dev))
		return 0;

	if (ts) {
		goodix_ts_resume(ts);
	}

	return 0;
}

static struct dev_pm_ops gtp_pm_ops = {
	.suspend = gtp_pm_suspend,
	.resume = gtp_pm_resume,
};
#endif
#ifdef CONFIG_HAS_EARLYSUSPEND
/*******************************************************
Function:
	Early suspend function.
Input:
	h: early_suspend struct.
Output:
	None.
*******************************************************/
static void goodix_ts_early_suspend(struct early_suspend *h)
{
	struct goodix_ts_data *ts;
	s8 ret = -1;
	ts = container_of(h, struct goodix_ts_data, early_suspend);

	GTP_DEBUG_FUNC();

	GTP_INFO("System suspend.");

	ts->gtp_is_suspend = 1;
	g_suspend_flag = 1;
	dprintk(DEBUG_INIT, "%s g_suspend_flag=%d\n", __func__, g_suspend_flag);
#if GTP_ESD_PROTECT
	gtp_esd_switch(ts->client, SWITCH_OFF);
#endif

	//#if GTP_GESTURE_WAKEUP
	if (gtp_gesture_wakeup)
		ret = gtp_enter_doze(ts);
	else {
		//#else
		if (ts->use_irq) {
			gtp_irq_disable(ts);
		} else {
			hrtimer_cancel(&ts->timer);
		}
		ret = gtp_enter_sleep(ts);
	}
	//#endif
	if (ret < 0) {
		GTP_ERROR("GTP early suspend failed.");
	}
	// to avoid waking up while not sleeping
	//	delay 48 + 10ms to ensure reliability
	msleep(58);
	//#if (!GTP_GESTURE_WAKEUP)
	if (!gtp_gesture_wakeup) {
		printk("ctp power off in early suspend!!!\n");
		input_set_power_enable(&(config_info.input_type), 0);
		__gpio_set_value(config_info.wakeup_gpio.gpio, 0);
		//#endif
	}
}

/*******************************************************
Function:
	Late resume function.
Input:
	h: early_suspend struct.
Output:
	None.
*******************************************************/
static void goodix_ts_late_resume(struct early_suspend *h)
{
	struct goodix_ts_data *ts;
	s8 ret = -1;
	ts = container_of(h, struct goodix_ts_data, early_suspend);

	GTP_DEBUG_FUNC();

	GTP_INFO("System resume.");
	//#if (!GTP_GESTURE_WAKEUP)
	if (!gtp_gesture_wakeup) {
		__gpio_set_value(config_info.wakeup_gpio.gpio, 1);
		printk("ctp power on in late resume!!!\n");
		input_set_power_enable(&(config_info.input_type), 1);
		msleep(10);
	}
	//#endif

	ret = gtp_wakeup_sleep(ts);

	//#if GTP_GESTURE_WAKEUP
	if (gtp_gesture_wakeup)
		doze_status = DOZE_DISABLED;
	//#endif

	if (ret < 0) {
		GTP_ERROR("GTP later resume failed.");
	}
#if (GTP_COMPATIBLE_MODE)
	if (CHIP_TYPE_GT9F == ts->chip_type) {
		// do nothing
	} else
#endif
	{
		gtp_send_cfg(ts->client);
	}

	if (ts->use_irq) {
		gtp_irq_enable(ts);
	} else {
		hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
	}

	ts->gtp_is_suspend = 0;
	g_suspend_flag = 0;
	dprintk(DEBUG_INIT, "%s g_suspend_flag=%d\n", __func__, g_suspend_flag);
#if GTP_ESD_PROTECT
	gtp_esd_switch(ts->client, SWITCH_ON);
#endif
}
#endif

#if GTP_ESD_PROTECT
s32 gtp_i2c_read_no_rst(struct i2c_client *client, u8 *buf, s32 len)
{
	struct i2c_msg msgs[2];
	s32 ret = -1;
	s32 retries = 0;

	GTP_DEBUG_FUNC();

	msgs[0].flags = !I2C_M_RD;
	msgs[0].addr = client->addr;
	msgs[0].len = GTP_ADDR_LENGTH;
	msgs[0].buf = &buf[0];
	// msgs[0].scl_rate = 300 * 1000;	 // for Rockchip, etc.

	msgs[1].flags = I2C_M_RD;
	msgs[1].addr = client->addr;
	msgs[1].len = len - GTP_ADDR_LENGTH;
	msgs[1].buf = &buf[GTP_ADDR_LENGTH];
	// msgs[1].scl_rate = 300 * 1000;

	while (retries < 5) {
		ret = i2c_transfer(client->adapter, msgs, 2);
		if (ret == 2)
			break;
		retries++;
	}
	if ((retries >= 5)) {
		GTP_ERROR("I2C Read: 0x%04X, %d bytes failed, errcode: %d!", (((u16)(buf[0] << 8)) | buf[1]), len - 2, ret);
	}
	return ret;
}

s32 gtp_i2c_write_no_rst(struct i2c_client *client, u8 *buf, s32 len)
{
	struct i2c_msg msg;
	s32 ret = -1;
	s32 retries = 0;

	GTP_DEBUG_FUNC();

	msg.flags = !I2C_M_RD;
	msg.addr = client->addr;
	msg.len = len;
	msg.buf = buf;
	// msg.scl_rate = 300 * 1000;	 // for Rockchip, etc

	while (retries < 5) {
		ret = i2c_transfer(client->adapter, &msg, 1);
		if (ret == 1)
			break;
		retries++;
	}
	if ((retries >= 5)) {
		GTP_ERROR("I2C Write: 0x%04X, %d bytes failed, errcode: %d!", (((u16)(buf[0] << 8)) | buf[1]), len - 2, ret);
	}
	return ret;
}
/*******************************************************
Function:
	switch on & off esd delayed work
Input:
	client:  i2c device
	on:		 SWITCH_ON / SWITCH_OFF
Output:
	void
*********************************************************/
void gtp_esd_switch(struct i2c_client *client, s32 on)
{
	struct goodix_ts_data *ts;

	ts = i2c_get_clientdata(client);
	spin_lock(&ts->esd_lock);

	if (SWITCH_ON == on) {  // switch on esd
		if (!ts->esd_running) {
			ts->esd_running = 1;
			spin_unlock(&ts->esd_lock);
			GTP_INFO("Esd started");
			queue_delayed_work(gtp_esd_check_workqueue, &gtp_esd_check_work, ts->clk_tick_cnt);
		} else {
			spin_unlock(&ts->esd_lock);
		}
	} else {	// switch off esd
		if (ts->esd_running) {
			ts->esd_running = 0;
			spin_unlock(&ts->esd_lock);
			GTP_INFO("Esd cancelled");
			cancel_delayed_work_sync(&gtp_esd_check_work);
		} else {
			spin_unlock(&ts->esd_lock);
		}
	}
}

/*******************************************************
Function:
	Initialize external watchdog for esd protect
Input:
	client:  i2c device.
Output:
	result of i2c write operation.
		1: succeed, otherwise: failed
*********************************************************/
static s32 gtp_init_ext_watchdog(struct i2c_client *client)
{
	u8 opr_buffer[3] = {0x80, 0x41, 0xAA};
	GTP_DEBUG("[Esd]Init external watchdog");
	return gtp_i2c_write_no_rst(client, opr_buffer, 3);
}

/*******************************************************
Function:
	Esd protect function.
	External watchdog added by meta, 2013/03/07
Input:
	work: delayed work
Output:
	None.
*******************************************************/
static void gtp_esd_check_func(struct work_struct *work)
{
	s32 i;
	s32 ret = -1;
	struct goodix_ts_data *ts = NULL;
	u8 esd_buf[5] = {0x80, 0x40};

	GTP_DEBUG_FUNC();

	ts = i2c_get_clientdata(i2c_connect_client);

	if (ts->gtp_is_suspend) {
		GTP_INFO("Esd suspended!");
		return;
	}

	for (i = 0; i < 3; i++) {
		ret = gtp_i2c_read_no_rst(ts->client, esd_buf, 4);

		GTP_DEBUG("[Esd]0x8040 = 0x%02X, 0x8041 = 0x%02X", esd_buf[2], esd_buf[3]);
		if ((ret < 0)) {
			// IIC communication problem
			continue;
		} else {
			if ((esd_buf[2] == 0xAA) || (esd_buf[3] != 0xAA)) {
				// IC works abnormally..
				u8 chk_buf[4] = {0x80, 0x40};

				gtp_i2c_read_no_rst(ts->client, chk_buf, 4);

				GTP_DEBUG("[Check]0x8040 = 0x%02X, 0x8041 = 0x%02X", chk_buf[2], chk_buf[3]);

				if ((chk_buf[2] == 0xAA) || (chk_buf[3] != 0xAA)) {
					i = 3;
					break;
				} else {
					continue;
				}
			} else {
				// IC works normally, Write 0x8040 0xAA, feed the dog
				esd_buf[2] = 0xAA;
				gtp_i2c_write_no_rst(ts->client, esd_buf, 3);
				break;
			}
		}
	}
	if (i >= 3) {
#if GTP_COMPATIBLE_MODE
		if (CHIP_TYPE_GT9F == ts->chip_type) {
			if (ts->rqst_processing) {
				GTP_INFO("Request processing, no esd recovery");
			} else {
				GTP_ERROR("IC working abnormally! Process esd recovery.");
				esd_buf[0] = 0x42;
				esd_buf[1] = 0x26;
				esd_buf[2] = 0x01;
				esd_buf[3] = 0x01;
				esd_buf[4] = 0x01;
				gtp_i2c_write_no_rst(ts->client, esd_buf, 5);
				msleep(50);
				gtp_esd_recovery(ts->client);
			}
		} else
#endif
		{
			GTP_ERROR("IC working abnormally! Process reset guitar.");
			esd_buf[0] = 0x42;
			esd_buf[1] = 0x26;
			esd_buf[2] = 0x01;
			esd_buf[3] = 0x01;
			esd_buf[4] = 0x01;
			gtp_i2c_write_no_rst(ts->client, esd_buf, 5);
			msleep(50);
			gtp_reset_guitar(ts->client, 50);
			msleep(50);
			gtp_send_cfg(ts->client);
		}
	}

	if (!ts->gtp_is_suspend) {
		queue_delayed_work(gtp_esd_check_workqueue, &gtp_esd_check_work, ts->clk_tick_cnt);
	} else {
		GTP_INFO("Esd suspended!");
	}
	return;
}
#endif

static const struct i2c_device_id goodix_ts_id[] = {{CTP_NAME, 0}, {}};

#if !USE_DETECT
static const struct of_device_id goodix_of_match[] = {
	{.compatible = "allwinner,goodix"},
	{},
};
#endif

static struct i2c_driver goodix_ts_driver = {
	.class = I2C_CLASS_HWMON,
	.probe = goodix_ts_probe,
	.remove = goodix_ts_remove,
	.id_table = goodix_ts_id,
	.driver = {
#if !USE_DETECT
		.of_match_table = goodix_of_match,
#endif
		.name = CTP_NAME,
		.owner = THIS_MODULE,
#ifndef CONFIG_HAS_EARLYSUSPEND
#if defined(CONFIG_PM)
		.pm = &gtp_pm_ops,
#endif
#endif
	},
	.address_list = normal_i2c,
#if USE_DETECT
	.detect = ctp_detect,
#endif
};

static int ctp_get_system_config(void)
{
	ctp_print_info(config_info, DEBUG_INIT);
	twi_id = config_info.twi_id;
	screen_max_x = config_info.screen_max_x;
	screen_max_y = config_info.screen_max_y;
	revert_x_flag = config_info.revert_x_flag;
	revert_y_flag = config_info.revert_y_flag;
	exchange_x_y_flag = config_info.exchange_x_y_flag;
	if ((screen_max_x == 0) || (screen_max_y == 0)) {
		printk("%s:read config error!\n", __func__);
		return 0;
	}
	return 1;
}

static int startup(void)
{
	s32 ret = -1;

	dprintk(DEBUG_INIT, "GTP driver init\n");
	printk("GTP driver init func = %s,line = %d \n", __func__, __LINE__);
	if (!input_sensor_startup(&(config_info.input_type))) {
		ret = input_sensor_init(&(config_info.input_type));
		if (ret != 0) {
			pr_err("%s:ctp_ops.input_sensor_init err.\n", __func__);
			return -1;
		}
		input_set_power_enable(&(config_info.input_type), 1);
	} else {
		pr_err("%s: input_ctp_startup err.\n", __func__);
		return -1;
	}
	if (config_info.ctp_used == 0) {
		printk("*** ctp_used set to 0 !\n");
		printk("*** if use ctp,please put the sys_config.fex ctp_used set to 1. \n");
		return -1;
	}

	printk("GTP driver init func = %s,line = %d \n", __func__, __LINE__);
	if (config_info.ctp_gesture_wakeup == 1) {
		gtp_gesture_wakeup = 1;
		dprintk(DEBUG_INIT, "GTP driver gesture wakeup is used!\n");
	}

	if (!gtp_gesture_wakeup)
		gtp_power_ctrl_sleep = 1;

	if (!ctp_get_system_config()) {
		printk("%s:read config fail!\n", __func__);
		return -1;
	}
	// gtp_io_init(20);
	printk("GTP driver init func = %s,line = %d \n", __func__, __LINE__);
	return 0;
}

/*******************************************************
Function:
		Driver Install function.
Input:
		None.
Output:
		Executive Outcomes. 0---succeed.
********************************************************/
static int goodix_ts_init(void)
{
#if !USE_DETECT
	struct device_node *np;
	char *p;
#endif

#if USE_DETECT
	if (startup() != 0)
		return 0;
#else
	np = of_find_node_by_name(NULL, "ctp");
	printk("GTP driver init func = %s,line = %d \n", __func__, __LINE__);
	if (np && np->parent) {
		printk("GTP driver init func = %s,line = %d \n", __func__, __LINE__);
		p = (char *)np->parent->name;
		if (strncmp(p, "twi", 3) != 0) {
			gUseDetect = 1;
			if (startup() != 0)
				return 0;
			printk("GTP driver init func = %s,line = %d \n", __func__, __LINE__);
			goodix_ts_driver.detect = ctp_detect;
		}
	}
#endif
	goodix_wq = create_singlethread_workqueue("goodix_wq");
	if (!goodix_wq) {
		GTP_ERROR("Creat workqueue failed.");
		return -ENOMEM;
	}
#if GTP_ESD_PROTECT
	INIT_DELAYED_WORK(&gtp_esd_check_work, gtp_esd_check_func);
	gtp_esd_check_workqueue = create_workqueue("gtp_esd_check");
#endif

	printk("GTP driver init func = %s,line = %d \n", __func__, __LINE__);
	i2c_add_driver(&goodix_ts_driver);
	printk("GTP driver init func = %s,line = %d \n", __func__, __LINE__);
	if (config_info.probed)
		return 0;
	else
		return -1;
}

/*******************************************************
Function:
	Driver uninstall function.
Input:
	None.
Output:
	Executive Outcomes. 0---succeed.
********************************************************/
static void __exit goodix_ts_exit(void)
{
	dprintk(DEBUG_INIT, "GTP driver exited.");
	i2c_del_driver(&goodix_ts_driver);
	if (goodix_wq) {
		destroy_workqueue(goodix_wq);
	}
	input_sensor_free(&(config_info.input_type));
}

late_initcall(goodix_ts_init);
module_exit(goodix_ts_exit);
MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);

MODULE_DESCRIPTION("GTP Series Driver");
MODULE_LICENSE("GPL");
